(三)Spring 数据库开发

序言

  早期的互联网,JDBC 是访问数据库的主流选择,后面随着技术的不断发展,Hibernate、Mybatis 等 ORM 框架成为了更好选择。

  JDBC 存在什么缺陷,为什么使用 ORM 框架去替代它呢?

  Spring 和 ORM 框架又是什么关系?

  下面,跟随本文来了解下吧!

传统的 JDBC

  首先,我们来先看一下传统的 JDBC 连接方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    Class.forName("JDBC驱动类的名称");
Connection con = null;
PreparedStatement stmt = null;
try{
// 1.获取资源
con = DriverManager.getConnection(数据库连接字符串,数据库用户名,密码);
// 2.启动事务
con.setAutoCommit(false);
// 3.具体业务逻辑
stmt = con.prepareStatement("插入数据");

// 4.提交事务
con.commit();
} catch(Exception e){
try{
// 5.回滚事务
con.rollback();
} catch...
...
// 6.关闭资源
stmt.close();
con.close();

  如上述代码所示,JDBC 数据访问数据操作按以下流程进行:

  • ① 根据不同的数据库,需要注册不同的驱动,并填写一些基本信息(用户名密码)
  • ② 操作 Connection,打开 Statement 对象
  • ③ 通过 Statement 执行 SQL,返回结果到 ResultSet 对象
  • ④ 使用 ResultSet 读取数据,然后通过代码转化为具体的 POJO 对象
  • ⑤ 出现异常手动回滚事务
  • ⑥ 关闭数据库相关资源

缺陷

  通过传统的 JDBC 去连接数据源存在很多缺陷:

  • ① 每次都需要输入数据库的基本信息,可否抽离一次固定以复用?
  • ② Connection 是什么?每次都需要新建,大量创建与关闭会不会比较消耗资源,可否优化?
  • ③ 如何保证数据的读写正确性?即事务是如何管理控制的?
  • ④ SQL 语句定义、参数设置、结果集处理存在硬编码,不同的 SQL 零散地分布在各处,能否做成动态参数定义方式,并统一管理?
  • ⑤ 每次都需要手动映射获取的数据到 POJO 对象,好麻烦呀,能不能自动映射?

JDBC 的替代品——ORM 框架

  为了解决 JDBC 的缺陷,ORM 框架应运而生,比如 Mybatis:

  • 针对问题 ①、②,可以通过数据源连接池的方式进行配置及优化
  • 针对问题 ③,可以通过专门的事务控制器进行管理
  • 针对问题 ④,Mybatis 可以通过动态参数的形式定义到 mapper.xml 文件中并通过该类文件进行统一管理维护
  • 针对问题 ⑤,Mybatis 可自动将 SQL 执行结果映射至 POJO

Spring 与 ORM 间的关联

  不同的 ORM 本身当然可以独立地执行数据库相关操作,但对后端领域而言,ORM 仅仅只占一角,后端领域必须确定一个统筹者,而 Spring 作为 Java Web 领域的帝王,就构建了一个庞大的家族,Spring 可以对家族的各个成员发号施令,共同合作完成各种难题。

  那么,Spring 为了管理 DAO 层的各个 ORM 成员,必然确定一个顶层规范,让不同的 ORM 去进行遵守。

  所以,Spring 为 DAO 统一了哪些事物呢?

统一数据源

  不管使用何种持久化技术,都必须拥有数据连接。
  在 Spring 中,数据连接是通过数据源获得的,数据源既可以直接在 XML 文件中配置,也可以通过 Java 配置类的方式直接创建一个数据源。

统一异常体系

  统一的异常体系是整合不同的持久化技术的关键。
  Spring 提供了一套和实现技术无关的、面向 DAO 层语义的异常体系,并通过转换器将不同持久化技术的异常转换成 Spring 的异常。

  Spring 在org.springframework.dao包中提供了一套完备优雅的 DAO 异常体系,这些异常都继承于DataAccessException,该异常又继承于NestedRuntimeExceptionNestedRuntimeException异常以嵌套的方式封装了源异常。

  因此,虽然不同持久化技术的特定异常被转换到 Spring 的 DAO 异常体系中,但原始的异常信息并不会丢失;只要用户愿意,就可以方便地通过getCause()方法获取原始的异常信息。

  Spring 的 DAO 异常体系并不和具体的实现技术相关,它从 DAO 概念的抽象层面定义了异常的目录树。在所有的持久化框架中,并没有发现拥有如此丰富语义的异常体系的框架。

  Spring 的这种设计无疑是独具匠心的,它使得开发人员关注某一特定语义的异常变得很容易。在 JDBC 的SQLException中,用户必须通过异常的getErrorCode()getSQLState()方法获取错误代码,然后根据这些代码判断错误原因。这种过于底层的 API 不但带来了代码编程上的难度,而且也使代码的移植变得困难,因为getErrorCode()方法是数据库相关的。

  Spring 以分类手法建立了异常分类目录,对于大部分应用来说,这个异常分类目录对异常类型的划分具有适当的颗粒度。一方面,使开发者从底层细如针麻的技术细节中脱离出来:另一方面,可以从这个语义丰富的异常体系中选择感兴趣的异常加以处理。

统一数据操作模板

  Spring 为各种不同的 ORM 框架(Hibernate、Mybatis、JPA 等)都提供了模板和回调功能,不同的 ORM 在回调中编写具体的数据操作逻辑,使用模板执行数据操作。

  简而言之,Spring 通过模版模式定义了模版规范基类,统一了对数据库的数据操作,各种 ORM 继承实现重写即可。

  Spring 将这个相同的数据访问流程固化到模板中,并将数据访问中固定和变化的部分分开,同时保证模板类是线程安全的,以便多个数据访问线程共享同一个模板实例。固定的部分在模板类中已经准备好,而变化的部分通过回调接口开放处理,用于定义具体数据访问和结果返回的操作,如下图:

Spring Dao 模板和回调

  这样,只要编写好回调接口,并调用模板类进行数据访问,就能得到预期的结果:数据访问成功执行,前置和后置的样板化工作也按顺序正确执行,

  在提高开发效率的同时也保证了资源使用的正确性,彻底消除了因忘记资源释放而引起的资源泄露问题。

统一事务管理模版

  Spring 为事务管理提供了一致的编程模板, 在高层次建立了统一的事务抽象。

  换而言之,ORM 框架不管是选择 Spring JDBC、Hibernate、 JPA 还是选择 MyBatis,Spring 都可以让用户用统一的编程模型进行事务管理。

  与 Spring 为不同的 ORM 框架的数据操作实现提供了模板类思路相同, Spring 事务管理也继承了这一风格。

  Spring 提供了事务模板类TransactionTemplate,通过该类并配合使用事务回调TransactionCallback指定具体的持久化操作, 就可以通过编程方式实现事务管理,而无须关注资源获取、复用、释放、 事务同步和异常处理等操作。

参考

  • 陈雄华 林开雄 文建国. 精通 Spring 4.x 企业应用开发 [M]. 电子工业出版社,2017

文章信息

时间 说明
2019-03-11 初稿
2022-05-26 重构
0%