《精通Spring4.x 企业应用开发实战》第10章 Spring对Dao的支持

前言

汇总:《精通Spring4.x 企业应用开发实战》

本章主要是对Spring支持持久化技术的理论知识讲解,不涉及到源码解析。对源码解析感兴趣的小伙伴可以阅读另一篇文章《Spring源码深度分析》第8章 数据库连接JDBC。该篇文章主要以’JdbcTemplate‘作为示例进行源码解读。

一、深入理解‘DAO理念’

在日常开发中,我们习惯性的定义类似这样的目录结构:controller、service、dao。

我们大多数时候给DAO层的定义是:用于DB的交互。其实,抛开DB,DAO层也是可以与其他存储介质(OSS、File…)进行交互的。所以说,请不要把DAO仅仅限制为DB交互。

1.DAO的作用

DAO (Data Access Object)是用于访问数据的对象,DAO的作用有两处:
1、DAO屏蔽了数据存储的最终介质的不同,
2、DAO屏蔽了具体的实现技术的不同。

2.DAO存储介质

虽然在大多数情况下将数据保存在数据库中,但这并不是唯一的选择。数据存储的位置选择:

  1. 数据库
  2. 文件
  3. LDAP

二、数据库持久化技术

DAO层支持的存储介质有多种,现在我们以‘数据库’为主,进行讲解。

1.常见的持久化技术

下面列举常见的持久化技术:

  1. 传统JDBC;–仅关系型数据库
  2. Hibernate(底层是JDBC);–仅关系型数据库
  3. MyBatis(底层是JDBC);–仅关系型数据库
  4. JPA(底层是JDBC);
  5. JDO ;–可以在任何数据底层上运行

DAO层可以整合以上持久化技术,对数据库进行CRUD操作。

三、模板类

Spring使用模板类,提供了对持久化技术的支持。个人认为模板类的主要作用有两处:

  1. 简化数据库操作;
  2. 并发安全性:多个dao实例使用模板类是安全的;

1.使用模板类的好处

1.简化数据库操作

单独使用持久化技术的繁琐性

以上五种持久化技术,如果离开spring单独实现数据库操作,需要进行一系列繁琐的代码编写。以JDBC技术为例:
在这里插入图片描述在这里插入图片描述如上述代码所示,JDBC 数据访问操作按以下流程进行:
(1)准备资源。
(2) 启动事务。
(3)在事务中执行具体的数据访问操作。
(4)提交/回滚事务。
(5)关闭资源,处理异常

Spring提供模板类快速开发

按照传统的方式,在编写任何带事务的数据访问程序时,都需要重复编写上面的代码,而其中只有粗体部分所示的代码是业务相关的,其刚行公事,因而导致大量八股文式的代码充斥着整个程序。

Spring 为不同的持久化技术所提供的模板类。关于模板类的具体使用,后续博客会介绍。
在这里插入图片描述注:spring并没有提供对mybatis持久化技术的模板类。

下面以JdbcTemplate为例:
在这里插入图片描述

2.并发安全性

数据库操作的并发安全性,本质是’事务并发安全性‘。并发事务会给我们带来超出预期的错误:脏写、脏读、不可重复读、幻读。具体模板类是如何解决并发事务的,会在第11章讲解。

2.DaoSupport

如果直接使用模板类,则一般需要在 DAO 中定义一个模板对象并提供数据资源。Spring 为每种持久化技术都提供了支持类,支持类中己经完成了这样的功能。这样,只需扩展这些支持类,就可以直接编写实际的数据访问逻辑,因此更加方便。
不同持久化技术的支持类如表所示。
在这里插入图片描述
这些支持类都继承于 dao.support.DaoSupport 类,DaoSupport 类实现了 InitializingBean接口,在 afterPropertiesSet接口方法中检查模板对象和数据源是否被正确设置,否则将抛出异常。

DaoSupport如何使用,请参考链接:
https://blog.csdn.net/llussize/article/details/79444104

四、统一的异常体系

统一的异常体系是整合不同的持久化技术的关键。(无论是spring提供的模板类(jdbctemplate…),或者说spring整合的持久化技术(mybatis…),都是使用spring统一异常体系)
1、Spring 提供了一套和实现技术无关的、面向 DAO 层语义的异常体系,
2、并通过转换器将不同持久化技术的异常转换成Spring 的异常

1.为什么Spring必须提供自己的DAO异常体系

1、在很多正统 API 或框架中,检查型异常被过多地使用,以至在使用 API 时,代码里充斥着大量 try/catch 样板式的代码。在很多情况下,除在 try/catch 中记录异常信息外,并没有做多少实质性的工作。引发异常的问题往往是不可恢复的,如数据连接失败、SQL语句存在语法错误等。而强制捕捉的检查型异常除限制开发人员的自由外,并没有提供什么有价值的东西。
2、因此,Spring 的异常体系都是建立在运行期异常的基础上的,开发者可以根据需要捕捉感兴趣的异常。

JDK 的很多 API之所以难用,一个很大的原因就是检查型异常的泛滥,如 JavaMail、EJB、 JDBC 等。使用这些 API,一堆堆异常处理的代码喧宾夺主地侵入到业务代码中,破坏了代码的整洁和优雅。

2.Spring-DataAccessException

Spring 在tx模块‘spring-tx’中的 org.springfiramework.dao 包中提供了一套完备优雅的 DAO 异常体系,这些异常都继承于 DataAccessException, 而 DataAecessException 本身又继承于 NestedRuntimeException

NestedRuntimeException 异常以嵌套的方式封装了源异常。因此,虽然不同持久化技术的特定异常被转换到 Spring 的 DAO 异常体系中,但原始的异常信息并不会丢失;只要用户愿意,就可以方便地通过 getCause()方法获取原始的异常信息。【Java异常】Java异常监控重要手段 --异常链

3.异常转换器

1.传统JDBC 的异常转换器

传统的 JDBC API 在发生几乎所有的数据操作问题时都会抛出相同的 SQLException, 它将异常的细节性信息封装在异常属性中。所以,如果希望了解异常的具体原因,则必须分析异常对象的信息。
SQLException 拥有两个代表异常具体原因的属性:

  1. 错误码:是数据库相关的,可通过 getErrorCode()方法返回,其值的类型是 int;
  2. SQL 状态码:是一个标准的错误代码,可通过 getSQLState()方法返回,是一个 String 类型的值,由5个字符组成。

Spring 根据错误码和 SQL 状态码信息将 SQLException 译成 Spring DAO 的异常体系所对应的异常。在 org.springfiramework jdbc.support 包中定义了 SQLExceptionTranslator接口,该接口的两个实现类

  1. SQLErrorCodeSQLExceptionTranslator(jdbc模块):处理SQLException 中错误码的翻译工作;
  2. SQLStateSQLExceptionTranslator(jdbc模块):处理SQLException中的SQL状态码的翻译工作。

Spring 也支持 MyBatis ORM持久化技术,由于 MyBatis 抛出的异常是和传统JDBC 相同的 SQLException 异常,所以直接采用和 JDBC 相同的异常转换器。
在这里插入图片描述

2.其他持久化技术的异常转换器

由于各种框架级的持久化技术都拥有一个语义明确的异常体系,所以将这些异常转换为 Spring DAO 的体系相对轻松一些。下面将学习不同持久化技术的异常转换器。
在这里插入图片描述

五、数据源

不管采用何种持久化技术,都必须拥有数据连接。在 Spring 中,数据连接是通过数据源获得的。
1、在以往的应用中,数据源一般是由 Web 应用服务器提供的。
2、在 Spring 中,不但可以通过JNDI 获取应用服务器的数据源,也可以直接在 Spring 容器中配置数据源;
3、此外,还可以通过代码的方式创建一个数据源,以便进行无容器依赖的单元测试。

1.Spring提供的两种数据源

Spring 在第三方依赖包中包含了两个数据源的实现类包:其一是 Apache 的 DBCP;其二是 C3P0。可以在 Spring 配置文件中利用二者中的任何一个配置数据源。且都提供了数据库连接池技术。

除此之外,还有阿里提供的druid数据源,以及其他公司提供的数据源。

2.获取Connection连接的方式

获取Connection连接总共有两种方式:

  1. 直连方式(不会用到连接池)。 底层是用java.sql.Driver类获取一个Connection , Connection使用完后被close,断开与数据库的连接,我们称这总方式是直连数据库, 因为每次都需要重新建立与数据库之间的连接,而并没有把之前的Connection保留供下次使用。
    a:典型的有java.sql.DriverManager、org.springframework.jdbc.datasource.DriverManagerDataSource等。
  2. 池化方式(会用到连接池)。 使用javax.sql.DataSource,且DataSource内部也封装了一个连接池。当你获取DataSource的时候,它已经敲敲的与数据库建立了多个Connection, 并将这些Connection放入了连接池,此时调用DataSource.getConnection()它从连接池里取一个Connection返回,Connection使用完后被close, 但这个close并不是真正的与数据库断开连接,而是告诉连接池"我"已经被使用完,"你"可以把我分配给其它"人"使用了. 就这样连接池里的Connection被循环利用,避免了每次获取Connection时重新去连接数据库。
    a:典型的有C3P0、DBCP、Druid等。

DataSource与连接池的关系是: DataSource利用连接池缓存Connection,以达到系统效率的提升,资源的重复利用.

1.直连方式:DriverManager-java.sql

来自JDK中rt.jar包,位于java.sql包下的DriverManager类,提供了最原始的获取数据库连接的方式。

通过 DriverManager 类创建数据库连接对象 Connection。DriverManager 类作用于 Java 程序和 JDBC 驱动程序之间,用于检查所加载的驱动程序是否可以建立连接,然后通过它的 getConnection 方法根据数据库的 URL、用户名和密码,创建一个JDBC Connection 对象,例如:Connection connection = DriverManager.geiConnection(“连接数据库的 URL”,“用户名”,“密码”)。其中,URL-协议名+1P 地址(域名)+端口+数据库名称;用户名和密码是指登录数据库时所使用的用户名和密码。具体示例创建 MySQL 的数据库连接代码如下:
在这里插入图片描述

2.池化方式:DataSource-javax.sql

来自JDK中rt.jar包,位于javax.sql包下的DataSource类,使用DataSource对象是连接到数据源的首选方法。

其底层都是调用java.sql.Driver的方式获取数据库连接。

在DriverManager类的注释头中,找到这么一句描述:

The basic service for managing a set of JDBC drivers.  NOTE: The javax.sql.DataSource interface, new in the JDBC 2.0 API, provides another way to connect to a data source.  The use of a DataSource object is the preferred means of connecting to a data source.

翻译如下:

用于管理一组JDBC驱动程序的基本服务。注意:javax.sql.DataSource接口是JDBC 2.0 API中的新接口,它提供了另一种连接数据源的方法。使用DataSource对象是连接到数据源的首选方法。

注意,实现了javax.sql.DataSource接口,只是说具备了池化的条件,要想真正的实现池化,需要该实现类自己内部实现池化。

(1)DriverManagerDataSource-Spring的数据源实现类(非池化)

Spring 本身也提供了一个简单的数据源实现类 DriverManagerDataSource,它位于org.springframework-jdbe.datasource 包中。

这个类实现了 javax.sql.DataSource 接口,但它并没有提供池化连接的机制每次调用 getConnection()方法获取新连接时,只是简单地创建一个新的连接。因此,这个数据源类比较适合在单元测试或简单的独立应用中使用,因为它不需要额外的依赖类。
下面来看一下 DriverManagerDataSource 类的简单使用,如下:
在这里插入图片描述

(2)BasicDataSource-dbcp的数据源实现类(池化)

位于org.apache.commons.dbcp.BasicDataSource。


Connection con = BasicDataSource.getConnection();

2.获取Connection连接的方式

1.Java的方式

(1)DriverManager(不提供池化)

使用Java标准库中的DriverManager类来获取数据库连接。此方法适用于所有支持JDBC的数据库,并且不需要引入第三方库。获取连接的代码示例如下:

Connection connection = DriverManager.getConnection(url, username, password);
(2)DataSource(提供池化)

使用DataSource来获取数据库连接。DataSource是一个数据库连接池,它可以管理多个数据库连接,以便于高效地处理多个并发请求。DataSource还提供了连接池管理、连接池监控等功能,可以使应用程序更加稳定和高效。获取连接的代码示例如下:

DataSource dataSource = new MyDataSource();
Connection connection = dataSource.getConnection();
(3)JNDI(提供池化)

使用Java命名和目录接口(Java Naming and Directory Interface,JNDI)来获取数据库连接。JNDI是一种标准的Java API,用于管理各种命名和目录服务,包括LDAP、DNS、NIS等。通过JNDI获取数据库连接可以使应用程序更加灵活和可配置。获取连接的代码示例如下:

InitialContext ctx = new InitialContext();
DataSource dataSource = (DataSource) ctx.lookup("jdbc/mydatasource");
Connection connection = dataSource.getConnection();

这三种方式的区别主要在于连接池管理、连接池监控、应用程序的可配置性等方面。DriverManager可以快速地获取数据库连接,但不能管理连接池。DataSource是一种更加高级的获取数据库连接的方式,它可以管理连接池并提供连接池监控功能。JNDI可以让应用程序更加灵活和可配置,但是需要额外的配置工作。

对于较小的应用程序,使用DriverManager获取数据库连接可能是一个更加简单和有效的方法。对于需要处理大量并发请求的大型应用程序,使用DataSource或JNDI获取数据库连接可能是更好的选择,因为它们可以管理连接池并提供更高效的资源管理和监控。

2.Spring的方式

(1)DriverManagerDataSource(不提供池化)

使用Spring提供的DriverManagerDataSource来获取数据库连接。这是一种非常简单和基础的获取数据库连接的方法,它使用DriverManager来获取数据库连接,不需要引入其他依赖库。它是基于JDBC DriverManager获取数据库连接,使用简单,但不能实现连接池的功能。获取连接的代码示例如下:

DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/testdb");
dataSource.setUsername("root");
dataSource.setPassword("root");

Connection connection = dataSource.getConnection();
@Bean
public DataSource dataSource() {
    DriverManagerDataSource dataSource = new DriverManagerDataSource();
    dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
    dataSource.setUrl("jdbc:mysql://localhost:3306/test");
    dataSource.setUsername("root");
    dataSource.setPassword("password");
    return dataSource;
}

(2)ComboPooledDataSource(提供池化)

使用Spring提供的ComboPooledDataSource来获取数据库连接。这是一种比较常用的获取数据库连接的方法,它可以管理连接池,并提供一些连接池相关的配置参数。获取连接的代码示例如下:

ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/testdb");
dataSource.setUser("root");
dataSource.setPassword("root");

Connection connection = dataSource.getConnection();
(3)JndiObjectFactoryBean(提供池化)

使用Spring提供的JndiObjectFactoryBean来获取数据库连接。这是一种通过JNDI获取数据库连接的方法,可以使应用程序更加灵活和可配置。获取连接的代码示例如下:

JndiObjectFactoryBean factory = new JndiObjectFactoryBean();
factory.setJndiName("java:comp/env/jdbc/mydatasource");
factory.setResourceRef(true);
factory.afterPropertiesSet();

DataSource dataSource = (DataSource) factory.getObject();
Connection connection = dataSource.getConnection();
(4)HikariCP(提供池化)

HikariCP 是一个高性能的连接池,Spring框架提供了对其的支持。

@Bean
public DataSource dataSource() {
    HikariDataSource dataSource = new HikariDataSource();
    dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
    dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
    dataSource.setUsername("root");
    dataSource.setPassword("password");
    dataSource.setConnectionTimeout(30000);
    dataSource.setIdleTimeout(600000);
    dataSource.setMaxLifetime(1800000);
    dataSource.setMinimumIdle(2);
    dataSource.setMaximumPoolSize(10);
    return dataSource;
}

在实际应用中,应根据具体需求和情况选择合适的方法。对于小型应用程序,可以使用DriverManagerDataSource来获取数据库连接;对于大型应用程序,可以使用ComboPooledDataSource或JndiObjectFactoryBean来获取数据库连接,以获得更好的性能和可扩展性。

3.第三方工具

使用第三方数据源:Spring还支持集成第三方数据源,如Apache Commons DBCP、C3P0等。通过配置第三方数据源相关属性,然后使用Spring的JdbcTemplate或NamedParameterJdbcTemplate类来获取数据库连接。

总结

以JdbcTemplate类作为入口,快速梳理本章节的知识点。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

@来杯咖啡

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

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

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

打赏作者

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

抵扣说明:

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

余额充值