当在配置文件中配有多个事务管理器时,可用该属性指定选择哪个事务管理器。
propagation :
事务的传播行为。所谓事务的传播行为,是指在当前事务开启之前,已经有一个事务上下文存在了的情况(比如说我们给方法A和方法B分别加上了事务,然后在A中调用了B,这时就需要选择事务传播行为,使A做出期待的应对。
该属性包含的可选项如下 :
-
REQUIRED(默认):
- 若方法A调用时,上下文中存在事务,则加入当前事务
- 若方法A调用时,上下文中没有事务,则新建一个事务
- 当在方法A调用方法B时,方法A、B将使用同一个事务
- 如果方法B发生异常需要回滚时,将回滚整个事务
-
REQUIRED_NEW :
- 对于方法A与B,无论方法调用时是否存在事务,都会开启一个新的事务
- 且如果方法调用时存在事务,当前事务将被延缓
- 如果方法B发生异常,将不会导致方法A的回滚
-
NESTED :
- 与REQUIRED_NEW类似,但支持JDBC,不支持JPA或Hibernate
-
SUPPORTS :
- 如果方法调用时,上下文中已经开启了事务,就使用这个事务
- 如果方法调用时,上下文中没有事务,就不使用事务
-
NOT_SUPPORTS :
- 以非事务的方式运行,如果存在事务,在方法调用到结束阶段事务都将被挂起
-
NEVER :
-
MANDATORY :
- 强制方法在事务中执行,如果存在事务,则加入当前事务
- 如果不存在事务,则将抛出异常
isolation :
事务的隔离级别。所谓事务的隔离级别,是指若干个并发的事务之间的隔离程度,由低到高依次分别为 : read uncommited(读未提交)、read commited(读提交)、read repeatable(读重复)、serializable(序列化)
;这四个级别可以逐个解决 脏读、不可重复读、幻读
这几类问题。
该属性包含的可选项如下 :
-
READ_UNCOMMITED :
- 最低级别的事务隔离级别,它允许另一个事务可以看到这个事务未提交的数据;可导致脏读,不可重复读,以及幻读
-
READ_COMMITED :
- 保证一个事务提交之后才能被另一个事务读取;可防止脏读,但可能导致不可重复读和幻读
-
REPEATABLE_READ :
- 不仅能实现 READ_COMMITED 的功能,还能进行如下限制 :当A事务读取了一条数据时,B事务将不允许修改这条记录;阻止脏读和不可重复读,但可能导致幻读
-
SERIALIZABLE :
- 开销最大但最可靠的事务隔离级别,事务被处理为顺序执行;除了防止脏读、不可重复读之外,还避免了幻读
-
DEFUALT :
- 使用当前数据库的默认隔离级别,如 Oracle、SqlServer 是 READ_COMMITED,MySQL 是 REPEATABLE_READF
其中,脏读、不可重复读、幻读概念如下 :
-
脏读 :读取了未提交的数据
- 比如说A操作试图将账户余额修由2000修改为1000,B操作读取了该修改后,试图在原余额的基础上增加1000;在B操作执行期间,A操作发生错误导致回滚,账户余额又变回了2000;那么,当B提交操作后,账户余额本该有2000+1000=3000,但由于B读取了错误的数据,即脏数据,此时的账户余额就仅剩下1000+1000=2000了。
-
不可重复读 :前后多次读取,数据内容不一致
- 比如说,我们有一个操作A需要多次读取账户余额,在A的第一次读取中,余额为1000,这时另一个操作B将余额修改为了2000,那么在A的第二次读取中,就会发现余额变为了2000,和第一次读取的数据不一样了;系统在同一个操作中无法读取同一个数据,称为不可重复读。
-
幻读 :前后多次读取,数据总量不一致
- 比如说事务A在执行读取操作时,需要多次统计数据的总量,前一次查询数据总量后,此时B事务执行了新增数据的操作并提交,这时候A再去读取,就会像产生幻觉一样平白多读出了几条数据,因此称为幻读。
timeout :
事务的过期时间。默认为当前数据库的事务过期时间
readonly :
指定当前事务是否为只读事务,默认为false
rollbackFor :
指定哪个或哪些异常发生时,需要引起事务回滚,默认为Throwable的子类
noRollBackFor :
指定哪个或哪些异常发生时,不引起事务回滚
Spring中注解方式的事务实现机制
在应用系统调用声明 @Transactional 的目标方法时,Spring Framework :
- 默认使用 AOP 代理
- 在代码运行时生成一个代理对象
根据 @Transaction 的属性配置信息,这个代理对象将 :
- 决定该目标方法是否由拦截器 TransactionInterceptor 来拦截
TransactionInterceptor在拦截时,会 :
- 首先在目标方法开始执行之前创建并加入事务
- 然后执行目标方法
- 最后根据执行情况是否出现异常,利用抽象事务管理器 AbstarctPlatformTransactionManager 操作数据源 DataSource 提交或回滚事务。
其他注意事项
@Transaction只有应用到public方法才能有效
这是因为在使用 Spring AOP 代理时,Spring在调用 TransactionInterceptor 拦截器对方法进行拦截之前,CglibAopProxy 的一个内部类中的 intercept 方法会间接调用 AbstractFallbackTransactionAttributeSource(Spring 通过这个类获取 @Transaction 注解的事务属性配置属性信息)的 computeTransactionAttribute 方法。
这个方法会检查目标方法的修饰符是不是 public,如果不是,就不会获取 @Transaction 的属性配置信息,最终造成不会使用 TransactionInterceptor 来拦截改目标方法,从而导致事务管理不被进行。
避免 Spring 中的 AOP 自调用问题
在 Spring 的 AOP 代理下,只有当目标方法由外部调用的时候,该目标方法才能由 Spring 生成的代理对象来管理。若同一类中,其他某个没有 @Transaction 注解的方法调用了有 @Transaction 注解的方法,这个有 @Transaction 注解的方法的事务将被忽略,不会发生回滚。
比如如下这种情况,我们有一个Demo类,类中有两个方法 A 和 B,A 上声明了事务,而 B 上没有时,当 B 调用了 A,A 中的事务将不会生效。
@Server
public class Demo {
public void methodB() {
methodA();
}
@Transaction
public void methodA() {
// ...
}
}
复制代码
这是因为,当我们使用 AOP 进行拦截时,首先会创建一个 Demo 的代理类,这个时候我们的系统中就会存在两个 Demo 对象了 :一个是 目标 Demo 对象
,一个是这个生成的 代理 Demo 对象
。如果在代理类的 A 方法中调用代理类的 B 方法,这时候 AOP 拦截是可以生效的,但是,如果在代理类的 A 方法中调用目标类的 B 方法,这时候 AOP 拦截就不会生效了。
原文
https://juejin.im/post/5dbf7eb06fb9a020644a2d01