Spring声明式事务使用详情(知识点+案例)
目录
1、声明式事务的概念
2、Spring事务管理器
3、基于注解的Spring事务
4、Spring事务属性
4.1只读(Read-Only)
4.2事务超时(Timeout)
4.3事务异常回滚(rollbackFor)
4.4事务隔离级别(Isolation Level)
4.5事务的传播行为(Propagation)
在数据库操作中,事务是一组操作的集合,要么全部成功,要么全部失败。事务管理的目标是确保数据一致性和完整性。Spring 提供了强大的事务管理功能,简化了事务处理的复杂性。
1、声明式事务的概念
事务主要分为两类,一类是编程式事务,一类是声明式事务!
编程式事务是指手动编写程序来管理事务,即通过编写代码的方式直接控制事务的提交和回滚。
比如下面的例子
Connection conn = ...;
try {
// 开启事务:关闭事务的自动提交
conn.setAutoCommit(false);
// 核心操作
// 业务代码
// 提交事务
conn.commit();
}catch(Exception e){
// 回滚事务
conn.rollBack();
}finally{
// 释放数据库连接
conn.close();
}
编程式事务具体操作过程中,所有细节都需要程序员自己来完成,比较繁琐,且代码复用性不高。
而声明式事务是指使用注解或 XML 配置的方式来控制事务的提交和回滚
开发者只需要添加配置即可, 具体事务的实现由第三方框架实现,避免我们直接进行事务操作!
使用声明式事务可以将事务的控制和业务逻辑分离开来,提高代码的可读性和可维护性。
2、Spring事务管理器
在Spring框架中,事务管理器(Transaction Manager)是一个核心组件,它负责管理和控制事务的边界,确保数据的一致性和完整性。Spring提供了多种事务管理器,用于不同的事务处理场景和技术栈。
事务管理器的主要职责包括:
- 管理事务的边界:确定事务的开始和结束,以及事务的提交或回滚。
- 集成事务资源:如数据源、消息队列等,确保这些资源能够参与到事务中来。
- 控制事务的传播行为:在复杂的事务调用场景中,控制事务的传播行为,如是否需要在现有事务中运行、是否需要创建新事务等。
- 管理事务的隔离级别:设置事务的隔离级别,以防止并发事务之间的干扰。
- 处理异常和回滚:在事务执行过程中,如果遇到异常,则根据事务的属性决定是否需要回滚事务。
Spring为不同的持久化技术提供了不同的事务管理器实现类,如:
DataSourceTransactionManager
:用于JDBC和MyBatis等基于JDBC的持久化技术。JpaTransactionManager
:用于JPA(Java Persistence API)的持久化技术。HibernateTransactionManager
:专门用于Hibernate的持久化技术。JmsTransactionManager
:用于JMS(Java Message Service)消息队列的事务管理。
我们现在要使用的事务管理器是DataSourceTransactionManager,将来整合 JDBC方式、JdbcTemplate方式、Mybatis方式的事务实现!
DataSourceTransactionManager类中的主要方法:
- doBegin():开启事务
- doSuspend():挂起事务
- doResume():恢复挂起的事务
- doCommit():提交事务
- doRollback():回滚事务
3、基于注解的Spring事务
入门案例
第一步:导入依赖
Spring声明式事务对应依赖
- spring-tx: 包含声明式事务实现的基本规范(事务管理器规范接口和事务增强等等)
- spring-jdbc: 包含DataSource方式事务管理器实现类DataSourceTransactionManager
- spring-orm: 包含其他持久层框架的事务管理器实现类例如:Hibernate/Jpa等
<!-- 数据库驱动 和 连接池-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
<!-- spring-jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>6.0.6</version>
</dependency>
<!-- 声明式事务依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>6.0.6</version>
</dependency>
第二步:配置事务管理器
配置要声明事务的数据库
第三步:使用声明事务注解@Transactional
第四步:调用
4、Spring事务属性
4.1只读(Read-Only)
对一个查询操作来说,如果我们把它设置成只读,就能够明确告诉数据库,这个操作不涉及写操作。这样数据库就能够针对查询操作来进行优化。
设置方式
// readOnly = true把当前事务设置为只读 默认是false!
@Transactional(readOnly = true)
把事务设置成已读的话,接下来就只能进行数据库查询语句,不能进行数据库增删改。
补充:@Transactional注解放在类上,则事务会影响到类中的每一个方法。除非在方法上又设置了 @Transactional 注解。对一个方法来说,离它最近的 @Transactional 注解中的事务属性设置生效。
4.2事务超时(Timeout)
事务在执行过程中,有可能因为遇到某些问题,导致程序卡住,从而长时间占用数据库资源。而长时间占用资源,大概率是因为程序运行出现了问题(可能是Java程序或MySQL数据库或网络连接等等)。此时这个很可能出问题的程序应该被回滚,撤销它已做的操作,事务结束,把资源让出来,让其他正常程序可以执行。概括来说就是一句话:超时回滚,释放资源。
timeout设置事务超时时间,单位秒! 默认: -1 永不超时,不限制事务时间!
4.3事务异常回滚(rollbackFor)
默认只针对运行时异常回滚,编译时异常不回滚
我们可以设置事务所有异常回滚,rollbackFor =Exception.class
rollbackFor属性:指定哪些异常类才会回滚,默认是 RuntimeException and Error 异常方可回滚!
/**
* timeout设置事务超时时间,单位秒! 默认: -1 永不超时,不限制事务时间!
* rollbackFor = 指定哪些异常才会回滚,默认是 RuntimeException and Error 异常方可回滚!
* noRollbackFor = 指定哪些异常不会回滚, 默认没有指定,如果指定,应该在rollbackFor的范围内!
*/
@Transactional(readOnly = false,timeout = 3,rollbackFor = Exception.class)
public void changeInfo() throws FileNotFoundException {
studentDao.updateAgeById(100,1);
//主动抛出一个检查异常,测试! 发现不会回滚,因为不在rollbackFor的默认范围内!
new FileInputStream("xxxx");
studentDao.updateNameById("test1",1);
}
4.4事务隔离级别(Isolation Level)
数据库事务的隔离级别是指在多个事务并发执行时,数据库系统为了保证数据一致性所遵循的规定。常见的隔离级别包括:
- 读未提交(Read Uncommitted):事务可以读取未被提交的数据,容易产生脏读、不可重复读和幻读等问题。实现简单但不太安全,一般不用。
- 读已提交(Read Committed):事务只能读取已经提交的数据,可以避免脏读问题,但可能引发不可重复读和幻读。
- 可重复读(Repeatable Read):在一个事务中,相同的查询将返回相同的结果集,不管其他事务对数据做了什么修改。可以避免脏读和不可重复读,但仍有幻读的问题。
- 串行化(Serializable):最高的隔离级别,完全禁止了并发,只允许一个事务执行完毕之后才能执行另一个事务。可以避免以上所有问题,但效率较低,不适用于高并发场景。
不同的隔离级别适用于不同的场景,需要根据实际业务需求进行选择和调整。
4.5事务的传播行为(Propagation)
在 Spring 事务管理中,事务的传播行为(Transaction Propagation)定义了一个事务方法被另一个事务方法调用时,事务如何传播。
我们通过propagation属性去设置它的传播
事务传播propagation的默认值是REQUIRED
REQUIRED:如果父方法有事务,就加入,如果没有就新建自己独立!(就是说,如果父方法下调用的其他事务,如果其他事务异常回滚了,我也要跟着回滚,这是一个整体)
REQUIRES_NEW:不管父方法是否有事务,我都新建事务,都是独立的(如果父方法下调用的其他事务,如果其他事务异常回滚了,我们不用跟着回滚,我们是独立执行的)
注意:在同一个类中,对于@Transactional注解的方法调用,事务传播行为不会生效。这是因为Spring框架中使用代理模式实现了事务机制,在同一个类中的方法调用并不经过代理,而是通过对象的方法调用,因此@Transactional注解的设置不会被代理捕获,也就不会产生任何事务传播行为的效果