两部分Spring的JDBC模板,Spring的事务管理,这两部分是对ioc和aop的典例应用
jdbc模板是对ioc的应用
事务管理是对Aop的应用

一,JDBC模板

在JdbcTemplate中执行SQL语句的方法大致分为3类:

  1. execute:可以执行所有SQL语句,一般用于执行DDL语句。
  2. update:用于执行INSERT、UPDATE、DELETE等DML语句。
  3. queryXxx:用于DQL数据查询语句。
  • 操作关系型数据的:JdbcTemplate和HibernateTemplate。
  • 操作 nosql 数据库的:RedisTemplate。
  • 操作消息队列的:JmsTemplate。

xml文件

  1. 《注册DataSource
  2. <bean id="myDateSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  3. <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
  4. <property name="url" value="jdbc:mysql:///abc"/>
  5. <property name="username" value="root"/>
  6. <property name="password" value="123456"/>
  7. </bean>
  8. 可以用此bean代替下面俩
  9. { <bean id="studentDao" class="com.abc.dao.StudentDaoImpl">
  10. <property name="dataSource" ref="myDateSource"/>
  11. </bean> }
  12. 《注册JdbcTemplate
  13. <bean id="myJdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
  14. <property name="dataSource" ref="myDateSource"/>
  15. </bean>
  16. 《注册Dao
  17. <bean id="studentDao" class="com.abc.dao.StudentDaoImpl">
  18. <property name="jdbcTemplate" ref="myJdbcTemplate"/>
  19. </bean>
  20. 《注册service
  21. <bean id="studentService" class="com.abc.service.StudentServiceImpl">
  22. <property name="dao" ref="studentDao"/>
  23. </bean>

自定义对象的查询,

  1. public class StudentDaoImpl extends JdbcDaoSupport implements IStudentDao {
  2. public void insertStudent(Student student) {
  3. String sql="insert into student(name,age) values(?,?)";
  4. this.getJdbcTemplate().update(sql,student.getName(),student.getAge());
  5. }
  6. public void deleteStudentById(int id) {
  7. String sql="delete from student where id=?";
  8. this.getJdbcTemplate().update(sql,id);
  9. }
  10. public void updateStudent(Student student) {
  11. String sql="update student set name=?,age=? where id=?";
  12. this.getJdbcTemplate().update(sql,student.getName(),student.getAge(),student.getId());
  13. }
  14. public String selectStudentNameById(int id) {
  15. String sql="select id,name,age from student where id=?";
  16. Object[] arg= {id}; arg封装为一个数组
  17. return this.getJdbcTemplate().queryForObject(sql, arg,String.class);
  18. }
  19. public List<String >selectAllStudentsName() {
  20. String sql="select name from student";
  21. return this.getJdbcTemplate().queryForObject(sql,String.class);
  22. }
  23. public Student selectStudentById(int id) {
  24. String sql="select id,name,age from student where id=?";
  25. Object[] arg={id};
  26. return this.getJdbcTemplate().queryForObject(sql,arg, new StudentRowMapper());//行映射器
  27. }
  28. public List<Student> selectAllStudents() {
  29. String sql="select id,name,age from student ";
  30. return this.getJdbcTemplate().query(sql, new StudentRowMapper());//行映射器
  31. }
  32. }

行映射器(相当于将查询结果封装为一个类)

  1. public class StudentRowMapper implements RowMapper<Student> {
  2. //该方法的功能是:将查询结果集中的一行记录数据转换为指定的对象,该方法是由框架自动调用执行的
  3. //当查询出总的结果集后,框架会自动遍历这个总的结果集,每遍历一行记录,就会调用一次这个方法
  4. //也就是说,只要调用该方法了,就说明当前的结果集中还有数据
  5. //resultSet 并非是查询出的总结果集,而是这个总的结果集中的一行记录,
  6. //i :当前遍历行在查询出的总结果集的索引,
  7. public Student mapRow(ResultSet resultSet, int i) throws SQLException {
  8. Student student=new Student();
  9. student.setId(resultSet.getInt("id"));
  10. student.setName(resultSet.getString("name"));
  11. student.setAge(resultSet.getInt("age"));
  12. return student;
  13. }
  14. }
  1. //注册jdbcSorce:DBCP(需要导入依赖)
  2. <bean id="myDateSource" class="org.apache.commons.dbcp.BasicDateSource">
  3. 属性恰巧一样
  4. !-- //注册dataSource:Spring容器 只有在测试的时候才用-->
  5. <!-- <bean id="myDateSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">-->
  6. <!-- <bean id="myDateSource" class="org.mcange.v2.c3p0.ComboPooledDataSource">-->
  1. this.getJdbcTemplate().update(sql,student.getName(),student.getAge());
  2. 相当于,解除冗余 但不能这样用,因为jdbc模板是多例的,当线程结束时,会销毁这个模板对象
  3. //一个方法就是一个线程
  4. public class StudentDaoImpl extends JdbcDaoSupport implements IStudentDao {
  5. private JdbcTemplate jt;
  6. public StudentDaoImpl(){
  7. jt=this.getJdbcTemplate();
  8. }
  9. jt.update(sql,student.getName(),student.getAge());

1,步骤

  1. 为了避免直接使用 JDBC 而带来的复杂且冗长的代码,Spring 提供了一个强有力的模板 类---JdbcTemplate 来简化 JDBC 操作。并且,数据源 DataSource 对象与模板 JdbcTemplate 象均可通过 Bean 的形式定义在配置文件中,充分发挥了依赖注入的威力。

1 ,1导入 Jar 包

除了 Spring 的基本 Jar 包,数据库驱动 Jar 外,还需要导入两个 Jar 包。它们均在 Spring 框架解压目录下的 libs 目录中。
Spring和JDBC模板 - 图1

1.2 搭建测试环境

(1)定义实体类 User

image.png

(2)定义数据库及表

image.png

(3)定义 IUserDao

image.png

(4)初步定义 UserDaoImpl

 这里仅仅定义一个 UserDaoImpl 类实现了 IUserDao 接口,但不具体写每个方法的方法实 现。保持默认即可。后面会逐个通过 Jdbc 模板来实现。  <br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/513034/1637025155563-09a1011c-b451-4550-8aa4-1e01083dcdb7.png#clientId=u89388997-19e2-4&from=paste&height=146&id=ucaae3c95&margin=%5Bobject%20Object%5D&name=image.png&originHeight=292&originWidth=498&originalType=binary&ratio=1&size=53741&status=done&style=none&taskId=u158a53b0-4651-4700-b3f9-b6c4f27520a&width=249)

(5)定义 IUserService

image.png

(6)定义 UserService

image.png

(7)定义测试类 MyTest

image.png

1.3 数据源的配置

 使用 JDBC 模板,首先需要配置好数据源,数据源直接以 Bean 的形式配置在 Spring 配 置文件中。根据数据源的不同,其配置方式不同。下面主要讲解三种常用数据源的配置方式: (1)Spring 默认的数据源 <br />(2)DBCP 数据源 <br />(3)C3P0 数据源  

(1)Spring 默认的数据源 DriverManagerDataSource

Spring 默认的数据源为 DriverManagerDataSource,其有一个属性 DriverClassName,用于 接收 DB 驱动。
Ctrl + O 查看类结构及源码:
image.png
DriverManagerDataSource 类继承自 AbstractDriverBasedDataSource。其有三个属性用于 接收连接数据库的 URL、用户名与密码。
Ctrl + O 查看父类的类结构及源码:
image.png
image.png

(2)DBCP 数据源 BasicDataSource

DBCP,DataBase Connection Pool,是 apache 下的项目,使用该数据源,需要导入两个 Jar 包。它们在 Spring 依赖库的解压目录的 org.apache.commons 目录中 dbcp 与 pool 子包中。
com.springsource.org.apache.commons.dbcp-1.2.2.osgi.jar com.springsource.org.apache.commons.pool-1.5.3.jar

DBCP 数据源是 BasicDataSource,Ctrl + O 查看其类结构可看到,其有 driverClassName、 url、username、password 四个 DB 连接属性。
image.png

(3)C3P0 数据源 ComboPooledDataSource

 使用C3P0数据源,需要导入一个Jar包,在Spring依赖库的解压目录的com.mchange.c3p0 目录。  <br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/513034/1637025466259-ccc2e9e6-e9ed-4823-a25f-12fee8f19529.png#clientId=u89388997-19e2-4&from=paste&height=113&id=u1619b92e&margin=%5Bobject%20Object%5D&name=image.png&originHeight=225&originWidth=652&originalType=binary&ratio=1&size=78121&status=done&style=none&taskId=ubbc5e574-8a81-4a21-8c2a-15aee5b927d&width=326)<br /> C3P0 数据源是 ComboPooledDataSource ,Ctrl + O 查看其类结构可看到,其有 driverClass、 jdbcUrl、user、password 四个 DB 连接属性  <br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/513034/1637025482426-bde66dc3-253f-400c-9c94-551dd62e9124.png#clientId=u89388997-19e2-4&from=paste&height=78&id=u8674ce4d&margin=%5Bobject%20Object%5D&name=image.png&originHeight=156&originWidth=781&originalType=binary&ratio=1&size=58075&status=done&style=none&taskId=u9ac41fc7-76c8-4a09-abe1-a997a91c824&width=390.5)

1.4 从属性文件读取数据库连接信息

 为了便于维护,可以将数据库连接信息写入到属性文件中,使 Spring 配置文件从中读取 数据。 <br />属性文件名称随意,但一般都是放在 src 下  <br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/513034/1637025605016-3eb3d4df-b86f-4279-b79c-1ac01dd709b6.png#clientId=u89388997-19e2-4&from=paste&height=79&id=u29836391&margin=%5Bobject%20Object%5D&name=image.png&originHeight=157&originWidth=452&originalType=binary&ratio=1&size=28644&status=done&style=none&taskId=u14157873-9b4b-4dda-bdb1-f8fcf058d6a&width=226)<br /> Spring 配置文件从属性文件中读取数据时,需要在的 value 属性中使用${ }, 将在属性文件中定义的 key 括起来,以引用指定属性的值。 <br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/513034/1637025619648-6c5c11bd-4138-47d6-8f8a-44dbfd75c228.png#clientId=u89388997-19e2-4&from=paste&height=70&id=u1ce515a2&margin=%5Bobject%20Object%5D&name=image.png&originHeight=140&originWidth=784&originalType=binary&ratio=1&size=57531&status=done&style=none&taskId=uc9b6aff2-cbe5-4974-81e3-47d91ac9945&width=392)<br /> 该属性文件若要被 Spring 配置文件读取,其必须在配置文件中进行注册。注册方式有两 种: (1)<bean/>方式 (2)<context/>方式 

(1)方式-使用 class 为 PropertyPlaceholderConfigurer

以 PropertyPlaceholderConfigurer 类的 bean 实例的方式进行注册。该类有一个属性 location,用于指定属性文件的位置。这种方式不常用。
image.png

(2)< context:property-placeholder/>方式

该方式要求在 Spring 配置文件头部加入 context 的约束,即修改配置文件头。
image.png
< context:property-placeholder/>标签中有一个属性 location,用于指定属性文件的位置。
image.png

1.5 配置 JDBC 模板

JDBC 模板类 JdbcTemplate 从其父类 JdbcAccessor 继承了一个属性 dataSource,用于接收 数据源。
查看 JdbcTemplate 源码,及 JdbcAccessor 的类结构:
image.png
image.png
image.png

1.6 Dao 实现类继承 JdbcDaoSupport 类

 JdbcDaoSupport 类中有一个属性 JdbcTemplate,用于接收 JDBC 模板。所以 Dao 实现类 继承了 JdbcDaoSupport 类后,也就具有了 JDBC 模板属性。在配置文件中,只要将模板对象   注入即可。  <br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/513034/1637025980030-cdec42d2-ed0e-4fed-9430-ebcbb05f1512.png#clientId=u89388997-19e2-4&from=paste&height=213&id=ubd21cc21&margin=%5Bobject%20Object%5D&name=image.png&originHeight=425&originWidth=522&originalType=binary&ratio=1&size=114492&status=done&style=none&taskId=ud6a23ede-f924-4cea-89cb-8d047e74403&width=261)<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/513034/1637025991801-ad6c8d5f-8e0a-4db6-91ee-4bb0d8bf16d2.png#clientId=u89388997-19e2-4&from=paste&height=133&id=udd0f9201&margin=%5Bobject%20Object%5D&name=image.png&originHeight=266&originWidth=699&originalType=binary&ratio=1&size=83329&status=done&style=none&taskId=u8f008cea-38c7-4172-942e-e0ef567126a&width=349.5)<br /> 再仔细查看 JdbcDaoSupport 类,发现其有一个 dataSource 属性,查看 setDataSource() 方法体可知,若 JDBC 模板为 null,则会自动创建一个模板对象。  <br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/513034/1637026003256-9f214e44-d867-4708-98ff-6299d1eec807.png#clientId=u89388997-19e2-4&from=paste&height=70&id=u2294c946&margin=%5Bobject%20Object%5D&name=image.png&originHeight=139&originWidth=792&originalType=binary&ratio=1&size=45176&status=done&style=none&taskId=u33f03d11-f74d-40cc-b14d-fc6fcc75ed7&width=396)<br /> 故,在 Spring 配置文件中,对于 JDBC 模板对象的配置完全可以省去,而是在 Dao 实现 类中直接注入数据源对象。这样会让系统自动创建 JDBC 模板对象  <br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/513034/1637026021465-e06e2a2b-be11-43ff-b6f0-534583bda81b.png#clientId=u89388997-19e2-4&from=paste&height=65&id=uba122337&margin=%5Bobject%20Object%5D&name=image.png&originHeight=130&originWidth=743&originalType=binary&ratio=1&size=41293&status=done&style=none&taskId=uf41e7fba-2300-4d17-b297-9317a52bf00&width=371.5)

1.7 对 DB 的增、删、改操作

 JdbcTemplate 类中提供了对 DB 进行修改、查询的方法。Dao 实现类使用继承自 JdbcDaoSupport 的 getTemplate()方法,可以获取到 JDBC 模板对象。  <br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/513034/1637026061760-7ea8cd67-813e-405d-804c-eec4a78ce179.png#clientId=u89388997-19e2-4&from=paste&height=138&id=u42286bf1&margin=%5Bobject%20Object%5D&name=image.png&originHeight=275&originWidth=513&originalType=binary&ratio=1&size=66689&status=done&style=none&taskId=u02e525ea-ec7c-4faa-8430-587c7865321&width=256.5)<br /> 对 DB 的增、删、改都是通过 update()方法实现的。该方法常用的重载方法有两个:  public int update ( String sql) <br />public int update ( String sql, Object… args) <br />第 1 个参数为要执行的 sql 语句,第 2 个参数为要执行的 sql 语句中所包含的动态参数。 其返回值为所影响记录的条数。一般不用  <br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/513034/1637026102393-aee7bc0d-b88c-43ed-b9c2-2fd613033544.png#clientId=u89388997-19e2-4&from=paste&height=182&id=u0c193daf&margin=%5Bobject%20Object%5D&name=image.png&originHeight=364&originWidth=782&originalType=binary&ratio=1&size=100768&status=done&style=none&taskId=u1407ef3f-2abf-4143-abdb-23046b8ee6e&width=391)

1.8 对 DB 的查询操作

JDBC 模板的查询结果均是以对象的形式返回。根据返回对象类型的不同,可以将查询 分为两类:简单对象查询,与自定义对象查询。
简单对象查询:查询结果为 String、Integer 等简单对象类型,或该类型做为元素的集合 类型,如 List等。
自定义对象查询:查询结果为自定义类型,如 User 等,或该类型做为元素的集合类型, 如 List等。

(1)简单对象查询

常用的简单对象查询方法有:查询结果为单个对象的 queryForObject()与查询结果为 List 的 queryForList()。
pubic T queryForObject (String sql, Class type, Object… args)
pubic List queryForList (String sql, Class type, Object… args)
image.png

(2)自定义对象查询

常用的自定义对象查询方法有:查询结果为单个对象的 queryForObject()与查询结果为 List 的 query()。
pubic T queryForObject (String sql, RowMapper m , Object… args)
pubic List query (String sql, RowMapper m, Object… args)
注意,RowMapper 为记录映射接口,用于将查询结果集中每一条记录包装为指定对象。 该接口中有一个方法需要实现:
public Object mapRow(ResultSet rs, int rowNum)
参数 rowNum 表示总的结果集中当前行的行号,但参数 rs 并不表示总的结果集,而是 表示 rowNum 所代表的当前行的记录所定义的结果集,仅仅是当前行的结果。
一般,该方法体中就是实现将查询结果中当前行的数据包装为一个指定对象。
image.png
image.png

1.9 注意:JDBC 模板对象是多例的

JdbcTemplate 对象是多例的,即系统会为每一个使用模板对象的线程(方法)创建一个 JdbcTemplate 实例,并且在该线程(方法)结束时,自动释放 JdbcTemplate 实例。所以在每 次使用 JdbcTemplate 对象时,都需要通过 getJdbcTemplate()方法获取。