Spring框架(下)JdbcTemplate、声明式事务管理
(一)使用JdbcTemplate
1.概述
- 为了使JDBC更加易于使用,Spring在JDBC API上定义了一个抽象层,以此建立一个JDBC存取框架。
- 作为Spring JDBC框架的核心,JDBC模板的设计目的是为不同类型的JDBC操作提供模板方法,通过这种方式,可以在尽可能保留灵活性的情况下,将数据库存取的工作量降到最低。
- 可以将Spring的JdbcTemplate看作是一个小型的轻量级持久化层框架,和我们之前使用过的DBUtils风格非常接近。
2.环境准备
2.1 导入jar包
1.IOC容器需要的jar包:
commons-logging-1.1.1.jar
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELEASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jar
2.JdbcTemplate需要的jar包:
spring-jdbc-4.0.0.RELEASE.jar
spring-orm-4.0.0.RELEASE.jar
spring-tx-4.0.0.RELEASE.jar
3.数据库驱动和数据源
druid-1.1.9.jar
mysql-connector-java-5.1.7-bin.jar
2.2 写连接数据库的配置文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm
jdbc.username=root
jdbc.password=073838
2.3 写JdbcTemplate的配置文件
2.4 测试
3.使用JdbcTemplate进行增删改
数据库的表:
3.1 单个增删改
3.2 批量增删改
注意事项:使用批量增删改时,where…in()语句不能使用通配符,模糊查询也不能使用通配符
3.3 查询单个数据
3.4 查询多个数据
(二)声明式事务管理
1.事务概述
-
在JavaEE企业级开发的应用领域,为了保证数据的完整性和一致性,必须引入数据库事务的概念,所以事务管理是企业级应用程序开发中必不可少的技术。
-
事务就是一组由于逻辑上紧密关联而合并成一个整体(工作单元)的多个数据库操作,这些操作要么都执行,要么都不执行。
-
事务的四个关键属性(ACID)
①原子性(atomicity):“原子”的本意是“不可再分”,事务的原子性表现为一个事务中涉及到的多个操作在逻辑上缺一不可。事务的原子性要求事务中的所有操作要么都执行,要么都不执行。
②一致性(consistency):“一致”指的是数据的一致,具体是指:所有数据都处于满足业务规则的一致性状态。一致性原则要求:一个事务中不管涉及到多少个操作,都必须保证事务执行之前数据是正确的,事务执行之后数据仍然是正确的。如果一个事务在执行的过程中,其中某一个或某几个操作失败了,则必须将其他所有操作撤销,将数据恢复到事务执行之前的状态,这就是回滚。
③隔离性(isolation):在应用程序实际运行过程中,事务往往是并发执行的,所以很有可能有许多事务同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。隔离性原则要求多个事务在并发执行过程中不会互相干扰。
④持久性(durability):持久性原则要求事务执行完成后,对数据的修改永久的保存下来,不会因各种系统错误或其他意外情况而受到影响。通常情况下,事务对数据的修改应该被写入到持久化存储器中。
2.Spring事务管理
2.1 编程式事务管理
- 使用原生的JDBC API进行事务管理
①获取数据库连接Connection对象
②取消事务的自动提交
③执行操作
④正常完成操作时手动提交事务
⑤执行失败时回滚事务
⑥关闭相关资源
- 评价
使用原生的****JDBC API*实现事务管理是所有事务管理方式的基石,同时也是最典型 的编程式事务管理。编程式事务管理需要将事务管理代码*嵌入到业务方法中*来控制事务 的提交和回滚。在使用编程的方式管理事务时,必须在每个事务操作中包含额外的事务 管理代码。相对于*核心业务*而言,事务管理的代码显然属于*非核心业务****,如果多个模块 都使用同样模式的代码进行事务管理,显然会造成较大程度的****代码冗余****。
2.2 声明式事务管理
大多数情况下声明式事务比编程式事务管理更好:它将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。
事务管理代码的固定模式作为一种横切关注点,可以通过AOP方法模块化,进而借助Spring AOP框架实现声明式事务管理。
Spring在不同的事务管理API之上定义了一个****抽象层****,通过****配置*的方式使其生效,从而让应用程序开发人员*不必了解事务管理API的底层实现细节****,就可以使用Spring的事务管理机制。
Spring既支持编程式事务管理,也支持声明式的事务管理。
2.3 Spring提供的事务管理器
Spring从不同的事务管理API中抽象出了一整套事务管理机制,让事务管理代码从特定的事务技术中独立出来。开发人员通过配置的方式进行事务管理,而不必了解其底层是如何实现的。
Spring的核心事务管理抽象是它为事务管理封装了一组独立于技术的方法。无论使用Spring的哪种事务管理策略(编程式或声明式),事务管理器都是必须的。
事务管理器可以以普通的bean的形式声明在Spring IOC容器中。
2.4 事务管理器的主要实现
-
DataSourceTransactionManager:在应用程序中只需要处理一个数据源,而且通过JDBC存取。
-
JtaTransactionManager:在JavaEE应用服务器上用JTA(Java Transaction API)进行事务管理
-
HibernateTransactionManager:用Hibernate框架存取数据库
3.测试数据准备
3.1 mvc三层
3.2 数据库表
1.book表
2.stock表
3.user表
3.3 具体的类
BookDao:
package com.deserts.spring.book.dao;
/**
* @ClassName BookDao
* @Description TODO
* @Author deserts
* @Date 2020/10/17 16:57
*/
public interface BookDao {
/**
* 查询价格
* @param bid
* @return Integer
*/
Integer queryPrice(String bid);
/**
* 设置库存
* @param bid
*/
void updateStock(String bid);
/**
* 设置余额
* @param uid
*/
void updateBalance(String uid, Integer price);
}
2.service层:BookServiceImpl
3.controller层:BookController
3.4 配置文件
主要是配置数据源和JdbcTemplate:
4.初步实现
1.配置事务管理器,开启注解驱动
2.在BookServiceImpl类的买书的方法中加上注解@Transactional,实现对两次更新数据库操作的事务管理
3.测试:
5.事务的传播行为
5.1 简介
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。事务的传播行为可以由传播属性指定。
举例:当该类的结账方法中调用了买单本书的方法,结账的事务调用了买书的事务
Spring定义了7种类传播行为。
事务传播属性可以在@Transactional注解的propagation属性中定义。
5.2 REQUIRED和REQUIREDS_NEW
1.REQUIRED传播行为
当bookService的buyBook()方法被另一个事务方法checkout()调用时,它默认会在现有的事务内运行。这个默认的传播行为就是REQUIRED。因此在checkout()方法的开始和终止边界内只有一个事务。这个事务只checkout()方法结束的时候被提交,结果用户一本书都买不了。
2.REQUIREDS_NEW传播行为
表示该方法必须启动一个新事务,并在自己的事务内运行。如果有事务在运行,就应该先挂起它。
6.事务的隔离级别
6.1 数据库并发问题
假设现在有两个事务:Transaction01和Transaction02并发执行。
1.脏读:针对某个字段的值
①Transaction01将某条记录的AGE值从20修改为30。
②Transaction02读取了Transaction01更新后的值:30。
③Transaction01回滚,AGE值恢复到了20。
④Transaction02读取到的30就是一个无效的值。
2.不可重复读:针对字段的值
①Transaction01读取了AGE值为20。
②Transaction02将AGE值修改为30。
③Transaction01再次读取AGE值为30,和第一次读取不一致。
3.幻读:针对表
①Transaction01读取了STUDENT表中的一部分数据。
②Transaction02向STUDENT表中插入了新的行。
③Transaction01读取了STUDENT表时,多出了一些行。
6.2 事务的隔离级别
1.读未提交 READ UNCOMMITTED 1
允许Transaction01读取Transaction02未提交的修改。会导致脏读。
2.读已提交 READ COMMITTED 2
要求Transaction01只能读取Transaction02已提交的修改。会导致不可重复读。
3.可重复读 REPEATABLE READ 4
确保Transaction01可以多次从一个字段中读取到相同的值,即Transaction01执行。会导致幻读
4.串行化 SERIALIZABLE 8
确保Transaction01可以多次从一个表中读取到相同的行,在Transaction01执行期间,禁止其它事务对这个表进行添加、更新、删除操作。可以避免任何并发问题,但性能十分低下。
5.各个隔离级别解决并发问题的能力:
6.各种数据库产品对事务隔离级别的支持程度
6.3 在Spring中指定事务的隔离级别
1.注解:在Transactional()的属性中设置isolation,如:
2.xml:可以在<tx:method>元素中指定隔离级别:
7.事务的超时timeout
超时:在事务强制回滚前最多可以执行(等待)的时间,可通过timeout属性来设置,如:
运行结果:
8.事务的只读
8.1 简介
readOnly:指定当前事务的一系列操作是否为只读。
注意:若设置为只读,mysql在访问数据时不加锁,可以提高性能,但要只加在只有读操作的事务中;若事务对数据库的操作不止有读,应该设置readOnly属性为false。
8.2 设置
1)注解:@Transactional注解的readOnly属性
2)xml:可以在<tx:method>元素中进行指定
9.触发事务回滚的条件
9.1 默认情况
捕获到RuntimeException或Error时回滚,而捕获到编译时异常不回滚。
9.2 设置途径
1)注解
① rollbackFor属性:指定遇到时必须进行回滚的异常类型,可以为多个
② noRollbackFor属性:指定遇到时不回滚的异常类型,可以为多个
2)xml
在Spring 2.x事务通知中,可以在<tx:method>元素中指定回滚规则。如果有不止一种异常则用逗号分隔。
10.xml方式配置事务
1.引入properties配置文件,创建数据源
2.引用数据源,配置事务管理器
3.配置事务通知,需要有数据源的支持
4.配置切入点表达式,将切入点表达式和事务属性配置关联到一起