《精通Spring4.x 企业应用开发实战》第13章 使用 SpringJDBC访问数据库

文章介绍了SpringJDBC如何降低使用JDBC的复杂度,通过JdbcTemplate简化数据库操作。讨论了在DAO中使用JdbcTemplate的方法,以及如何处理BLOB/CLOB类型数据。重点讲解了SpringJDBC对自增键的支持,包括序列和表方式产生主键,并强调了应用层主键方案的优点。最后提到了NamedParameterJdbcTemplate作为JdbcTemplate的增强,提供命名参数绑定功能。
摘要由CSDN通过智能技术生成

前言

Spring JDBC 是 Spring 所提供的持久层技术。它的主要目的是降低使用 JDBC API的门槛,以一种更直接、更简洁的方式使用 JDBC API。在 Spring JDBC 里,仅需做那些与业务相关的 DML操作的事,而将资源获取、Statement 创建、资源释放及异常处理等繁杂而乏味的工作交给 Spring JDBC。
虽然 ORM的框架已经成熟丰富,但 JDBC 的灵活、直接的特性,依然让它拥有自己的用武之地。如在完全依赖查询模型动态产生查询语句的综合查询系统中,Hiberate、MyBatis、 JPA 等框架都无法使用,这里 JDBC 是唯一的选择。

13.1 使用 Spring JDBC

但凡 Java 开发者都有过直接使用 JDBC 编写数据库程序的经历,由于 JDBC API过于底层,开发者不但需要编写数据操作代码,还需要编写获取 JDBC 连接、处理异常、释放资源等代码。即使一个再简单不过的数据库操作,也需要至少十几行代码。SpringJDBC 通过模板和回调机制大大降低了使用 JDBC 的复杂度,借由 Jdbe Template 的帮助,仅需要编写那些“必不可少”的代码就可以进行数据库操作。

13.1.1 JdbcTemplate小试牛刀

图片.png

13.1.2 在DAO 中使用 Jdbc Template

一般情况下都是在 DAO 类中使用 JdbcTemplate, JdbcTemplate 在 XML 配置文件中配置好后,直接在 DAO 中注入即可。
图片.png
按照相同的方式,就可以方便地创建其他的 DAO 类,如 TopicDao、 PostDao 等。在 Spring 配置文件中定义 Jdbc Template 并注入每个 DAO 中。
图片.png
图片.png
图片.png

13.2 基本的数据操作

图片.png

13.3 BLOB/CLOB类型数据

图片.png

13.4 自增键和行集

Spring JDBC 提供了对自增键及行集的支持,自增键对象让用户可以不依赖数据库的自增键,在应用层为新记录提供主键。在 Java 1.4 中引入了 RowSet,它允许在连接断开的情况下操作数据。本节将介绍如何在 Spring JDBC 中使用 RowSet。

13.4.1 自增键的使用

一般数据库都提供了自增键的功能,如 MySQL 的 auto increment、 SQL Server 的identity 字段等。Spring 允许用户在应用层产生主键值,为此定义了 org.springframework.jdbe.support.inerementer.DataFieldMax Valuelncrementer 接口,提供两种产生主键的方案:第一,通过序列产生主键;第二,通过表产生主键。根据主键产生方式及数据库类型的不同,Spring 提供了若干实现类,如图13-1 所示。
图片.png
根据不同的主键产生方式,可能需要配置表名、主键字段名或序列名等信息。下面以 Oracle 和 MySQL 数据库为例,分别讲解使用序列及表字段产生主键值的方式。
DataFieldMax Valuelncrementer 接口定义了3个获取下一个主键值的方法。

  • int nextIntValue():获取下一个主键值,主键数据类型为 int。
  • long nextLong Value(): 获取下一个主键值,主键数据类型为 long。
  • String nextString Value(): 获取下一个主键值,主键数据类型为 String。

在其抽象实现类 AbstractDataFieldMax ValueIncrementer 中,提供了几个重要的属性,其中 incrementerName 属性用于定义序列或模拟序列表的名称;如果返回的主键值是String 类型,则 paddingLength 属性可能会派上用场,它允许用户指定返回主键值的长度,不足的部分前面补 0。AbstractSequenceMax ValueIncrementer 类使用标准的数据库序列产生主键值,而 AbstractColumnMax ValueIncrementer 类使用一张模拟序列的表产生主键值。AbstractColumn Max ValueIncrementer 类可以通过 cacheSize 属性指定缓存的主键个数,当内存中的主键值用完后,递增器将一次性获取 cachesize 个主键值,这样就可以减少数据访问的次数,提高应用的性能。
在代码清单13-13 中,手工指定 tpost 表的主键值为1,第二次运行该方法时就会抛出主键冲突的异常,现在通过 DateFieldMax ValueInerementer 从数据库中获取主键值来弥补这个缺陷。调整 PostDao 的代码,添加 DateFieldMax Valuelncrementer 属性,并通过它从序列中得到下一个主键值。
图片.png

1.以序列方式产生主键值

图片.png

2.以表方式产生主键值

图片.png

13.4.2 如何规划主键方案

在实际应用中必须慎重规划好主键方案,它直接影响到应用系统的运行效率、扩展性及维护性等。一个欠考虑的主键方案不但会带来并发性的问题,还可能造成系统难于编程、不易维护。
从主键创建者的角度,可以粗略地将主键创建方案分为两类:其一为“应用层主键方案”,新数据的主键分配由应用层负责,如采用 UUID 或者使用 DataFieldMaxValuelncrementer 生成主键都属于这一类型;其二为“数据库层主键方案”,新数据的主键分配由数据库负责,即在定义表结构时将主键列设置为 auto increment,或者通过表的触发器分配主键。
笔者认为,“数据库层主键方案”已经成为历史遗留的产物,它的缺点和不足己经随着应用的发展不断凸显:其一,它会给应用开发带来不便,因为必须通过一个查询获取新增数据的主键值;其二,不方便主键值的全局管理和控制,使系统丧失灵活性;其三,不方便数据的整合和迁移。所以,除非有充足的理由,否则应当尽量避免采用“数据库层主键方案"。
在传统的应用中,数据表主键往往只承担本表内数据唯一性标识的任务。随着应用复杂性的不断提高,数据表主键开始肩负起更多的功能,包括数据库切分、资源定位等。
笔者从事的一个电力生产管理系统,对100多种电网设备提供信息管理,包括变电站、线路、标上塔、变压器等,每台设备对应一张表。系统各地区独立部署,但数据必须定时抽取到省公司中汇合。不但要求单个系统内设备表主键是唯一的,还必须要求全局内是唯一的,以便将各地区的设备表数据合并到一起而不产生主键冲突;否则就必须在数据整合时进行主键转换,而主键转换是极麻烦的事,会给系统带来很大的复杂度。
因此笔者当时采用了“应用层主键方案”,使用 UUID 为各设备表产生主键值,这样就可以保证设备 ID 的全局唯一性,为后期的数据整合带来便利。采用 UUID 的好处是,每台设备的 ID 都是唯一的,不但相同设备各地区的表数据合并不会有主键冲突问题,甚至不同设备的表数据合并也不会有主键冲突问题。
当然,采用 UUID 也有不好的地方,即 UUID 是一个36 位的字符串,会占用较多的存储空间。所以,另一个候选的主键方案就是采用分段长整型编码方案,将主键编码分为3段:第一段代表地区;第二段代表设备类型;第三段代表设备序号,这样就可以创建一个全局唯一的整数型主键值。当然,这时我们并不能直接使用 Spring 的DataFieldMax Valuelncrementer 完成主键创建的任务,因为 DataFieldMax Valuelncrementer只能为一张表创建主键。但道理是相通的,我们可以创建一张包含3个字段的主键表,这3 个字段分别存储地区编码、设备类型编码及当前主键最大值,编写一个类似于DataFieldMax Valuelncrementer 的接口以获取对应地区对应设备类型的主键值。

实战经验

在高并发的系统中,如果使用基于序列表的方式创建主键值,则应该考虑两个层面的并发问题:其一是应用层获取主键的并发问题,Spring 的 DataFieldMax Valuelncrementer实现类已经对获取主键值的代码进行了同步,因此保证了同一 JVM 内应用不会产生并发问题;其二是全局的并发问题,如果应用是集群部署的,所有集群节点都通过同一张序列表获取主键值,那么就必须对这张序列表进行乐观锁定(序列表必须添加一个版本或时间戳字段),以防止集群节点的并发问题。但是很可惜,Spring 的DataFieldMax Valuelncrementer 实现类并没有对序列表进行乐观锁定,因此,如果应用需要集群部署,那么 Spring 的 DataFieldMax Valuelncrementer 实现类是有全局并发问题的,必须自己实现 DataFieldMax ValueIncrementer 接口,以解决全局并发的问题。
另外,DataFieldMax Valuelncrementer 接口只能为一张表提供主键,即每张表都必须配置一个单独的 DataFieldMax ValueIncrementer,因此这种方案太死板了。如果需要采用序列表创建主键的方案,笔者建议参照 DataFieldMax Valuelncrementer 接口,自行编写一个可为多张表提供主键的接口。

13.4.3 以行集返回数据

图片.png

13.5 NamedParameterJdbcTemplate 模板类

除了标准的 JdbeTemplate 外,Spring 还提供了两个易用的 JDBC 模板类,分别是NamedParameterJdbcTemplate 和 SimpleJdbc Template。前者提供命名参数绑定的功能;而后者封装了 JdbcTemplate, 将常用的 API 开放出来。

NamedParameterJdbcTemplate具体使用:略。

13.6 小结

本章以 JdbcTemplate 模板类为中心,详细讲解了各种使用 JdbcTemplate 对数据进行CRUD 操作的方法。JdbcTemplate 使用大量的回调接口完成数据的访问操作,StatementCallback、 PreparedStatementCallback、 CallableStatementCallback、 BatchPreparedStatementSetter 及 RowMapper 是其中常用的回调接口,一般可以通过匿名内部类实现这些回调接口,使代码更加紧凑。
此外,还重点讲述了在 Spring JDBC 中操作 LOB 数据类型的方法。由于有些数据库使用了自己的 API 操作 LOB 数据,因此需要从数据源的代理 JDBC 对象中抽取本地数据库的 JDBC 对象。LOB 数据可以使用块数据和流数据的方式进行访问。如果 LOB数据比较大,则通过流操作方式可以减少内存的占用,同时保证程序的性能。
在本章中,我们花费了较大的篇幅讲述主键的知识,主键的产生方式从产生地点上可以分为应用层产生和数据库产生两种。应用层借助数据库的序列或表产生主键值,这种方式可以保证程序的可移植性和安全性,同时可以通过缓存机制提高运行效率。有些数据库支持数据表自增主键值产生机制,在 JDBC 3.0 以前的版本中,无法通过 Statement自动获取新增记录对应的主键值。这时需要在插入数据后,立即执行一条数据库相关的主键获取 SQL 语句以得到对应的主键值。在数据库高并发的情况下,有可能获取到不正确的主键值。在这种情况下,在插入数据前在应用层准备好主键值是很好的备选方案。
Spring 持续降低 Spring JDBC 的使用难度,它提供支持命名参数绑定的NamedParameterJdbcTemplate,以减少编程的出错概率。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@来杯咖啡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值