简介
首先讲明白,什么是事务传播行为?
本质上就是进入一个事务方法时,这个方法是否该用事务执行?该用哪个事务执行?
举个例子:
当A方法调用事务方法B时,B方法配置的事务传播行为就决定了方法B的事务情况。
以默认的required行为说明,如果进入方法B时,A方法已经开启了事务,那么方法B就会直接使用A方法的事务(等A方法提交)
如果A方法没有开启事务,那么B方法会开启一个新的事务。
至此,你已经理解了什么是事务传播行为。你也可以发现,事务的传播行为不仅仅由方法B的设置有关,还和进入方法B时,方法A有没有开启事务有关。因此,每种事务传播行为都有两种情况,即进入方法时有事务存在、进入方法时没有事务存在。
事务传播行为
Spring共提供了7种事务传播行为,每种传播行为都有两种情况,所以其执行情况如下表所示。
| 标题 | 不存在事务 | 存在事务 |
|---|---|---|
| required | 创建新事务 | 加入新事务 |
| required_new | 创建新事务 | 挂起当前事务,重新创建新事务 |
| supports | 不使用事务 | 加入事务 |
| mandatory | 抛异常 | 加入事务 |
| not_supports | 不使用事务 | 挂起当前事务,B还是以无事务方式运行 |
| never | 不使用事务 | 抛出异常 |
| nested | 创建新事务 | 使用嵌套事务 |
表格中出现了3种需要额外进行解释的行为。
1、加入事务:就是方法B和方法A共用一个事务,本质上等同于将方法B直接塞到了方法A中。任意地方导致回滚,A和B都会回滚。
2、挂起当前事务:就是B的事务和A的事务完全独立,B出现异常,B自己回滚(如果有新事务),A是否回滚仅仅取决于B是否抛出异常。如果A调用完B后的代码出现异常,则只回滚A,并不会影响B。
3、嵌套事务:实质上是JDBC的savepoint。A方法的事务更像是逻辑事务,在进入B时通过savepoint划分为多个物理事务。基本上等同于required_new,但是由于是逻辑事务,所以一方面只会有一次的事务操作语句,另一方面,不同之处在于如果A在调用完成B后的代码出错导致回滚是,会将B也进行回滚。
事务失效
事务失效的情况很多,很多人看到就头大,这可咋背呀。
其实,先搞懂Spring事务的原理就不难记了。
首先,事务操作的实现是通过aop实现的,然后回滚操作是通过捕获执行原方法的异常进行。
其次,事务的底层是使用了数据库的事务。
再者,线程安全实现是通过ThreadLocal实现,与当前线程绑定了。
等等……
所以,事务失效的情况,一定就是违反了其中的原理。
一、违反了aop原理的情况(你aop原理不知道是啥的话,emmm,再看看呗)
1、事务所在的bean没被IoC管理。(都没放到容器里,咋Aop?)
2、类内方法调用。(类内方法直接使用this调用,并不需要去IoC拿代理对象,所以也就不能被Aop)
3、事务方法被final等修饰,导致方法不可重写。(这个主要是因为@Transactional事务的代理方式是cglib,需要生成子类复写方法才能生效)
二、违反了异常的问题导致不能回滚的。
4、方法内捕获了异常。(因为要捕获异常,你不抛出来,怎能知道出问题了,所以不回滚了)
5、抛出的异常类型不对。(因为原理是try-catch原方法,但是Spring默认只catch了Error和RuntimeException,当然可以使用rollbackFor来指定额外的异常类型,如果你抛出了不在整个范围的异常,那么就会导致无法回滚。
三、违反了事务底层是数据库的事务。
6、数据库不支持事务。(数据库不支持,那还玩个der)
四、违反了线程
7、多线程下,得使用分布式事务了。单机事务不会生效。
等等……
所以,是不是也挺简单的。