Spring管理的事务是逻辑事务,而且物理事务和逻辑事务最大差别就在于事务传播行为,事务传播行为用于 指定在多个事务方法间调用时,事务是如何在这些方法间传播的。
下面以a.save()里调用了b.save()举例,事务方法之间调用时事务如何传播。
@Service
public class A {
@Autowired
private B b;
@Transactional(propagation=Propagation.XXX)
public void save() {
// ...
b.save();
// ...
}
}
@Service
public class B {
@Transactional(propagation=Propagation.XXX)
public void save() {
// ...
}
}
1. REQUIRED(默认)
默认的spring事务传播级别,使用该级别的特点是,如果上下文中已经存在事务,那么就加入到事务中执行,如果当前上下文中不存在事务,则新建事务执行。如下图:
由于两个方法属于同一个物理事务,如果发生回滚,则两者都回滚。
2. SUPPORTS
顾名思义就是可以支持事务,如果b.save()在事务环境中运行,则以事务形式运行,否则以非事务运行。
注:这里所谓的非事务形式的数据库访问只是指没有显式的事务边界而已,就是说数据库操作只是 auto-commit 的方式,在数据库的物理事务概念上,还是有事务的。譬如在这里,a.save()调用b.save(),如果当a是非事务执行(非手动提交事务,而是auto-commit),那么b.save()在执行前,a.save()的物理事务就要先提交,而同时,b.save()的物理事务也是auto-commit,这样才是这里说的b.save()以非事务方法运行,而不是指b.save()不开启数据库物理事务。而所谓的b.save()在事务环境中运行,是指当b的外层a.save()本身是手动提交事务时,b.save()也会包含在a.save()里边的同一个事务去执行,也就是说a.save()与b.save()的SQL操作在同一个物理事务中。
3. MANDATORY(强制的)
必须在一个事务中运行,也就是说,b.save()只能在已有事务的方法中被调用,如果当前事物不存在,会抛异常。
4. REQUIRES_NEW
总是会创建一个新事务(包括物理事务),该传播级别的特点是,每次都会新建一个事务,并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。如下图:
两个方法之间既不属于同一个逻辑事务也不属于同一个物理事务。
5. NOT_SUPPORTED
顾名思义不支持事务,当处于存在事务的上下文环境中运行时,b.save()会暂停当前已开启的事务,意味着a.save()的事务被挂起直至b.save()以非事务方法运行完毕后,a.save()的事务继续执行。
6. NEVER
绝不能在事务环境中运行,如果a.save()里声明了使用事务,而b.save()的事务类型声明为never,那么只能以抛异常告终。
与Mandatory相反,Mandatory意思是强制要求上下文中有事务(外层有事务),否则抛异常,而Never是上下文中不能有事务(外层无事务),否则抛异常。
7. NESTED
嵌套事务支持。该传播级别特征是,如果上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务。
Nested和RequiresNew的区别:
-
RequiresNew每次都创建新的独立的物理事务,而Nested只有一个物理事务;Nested嵌套事务回滚或提交不会导致外部事务回滚或提交,但外部事务回滚将导致嵌套事务回滚,而RequiresNew由于都是全新的事务,所以之间是无关联的
Nested使用JDBC 3的保存点实现,即如果使用低版本驱动将导致不支持嵌套事务 -
使用嵌套事务,必须确保具体事务管理器实现的nestedTransactionAllowed属性为true,否则不支持嵌套事务,如DataSourceTransactionManager默认支持,而HibernateTransactionManager默认不支持,需要我们来开启。
总结
-
死活不要事务的
NEVER:没有就非事务执行,有就抛出异常。
NOT_SUPPORTED:没有就非事务执行,有就直接挂起,然后非事务执行。 挂起记得B事务不能再操作同一张表,不然会死锁。 -
可有可无的
SUPPORTS: 有就用,没有就算了。 -
必须有事务的
REQUIRES_NEW: 有没有都新建事务,如果原来有,就将原来挂起。 外部内部事务互相隔离,互不影响,内层回滚不影响外部。
NESTED:如果没有,就新建一个事务;如果有,就在当前事务中嵌套其他事务。,外层默认事务,内层NESTED,外层调用try{内层}catch{}。 外层异常回滚外层+内层,内层异常仅回滚内层,不影响外层。
REQUIRED:如果没有,就新建一个事务;如果有,就加入当前事务。
MANDATORY:如果没有,就抛出异常;如果有,就使用当前事务。
参考:
https://fgu123.github.io/2019/03/19/Spring-Transaction-Propagation/ (Spring事务传播机制)
https://www.bilibili.com/video/BV1EE411p7dD?p=6