springboot下@transcation基本使用的几种可能
普通常使用的几种可能(事务的传播行为默认值Propagation.REQUIRED):
- @transcation只在使用方法A上,A内无调用其他方法,事务正常
- 方法A和方法B在同一个类下,@transcation都用在方法A和方法B上,A方法内调用B方法,A的事务正常,B的事务失效,且B的报错会导致A的事务进行回滚,即A和B都在A的事务中
- 方法A和方法B在同一个类下,@transcation只用在方法B上,A方法内调用B方法,B的事务失效,即使B方法报错也不会进行回滚,即B在报错前产生的脏数据会录入数据库
- 方法A和方法B不在同一个类下,@transcation都用在方法A和方法B上,A方法内调用B方法,会因为B方法的@transcation设置的事务传播性的值不同而不同,B方法的传播行为值为Propagation.REQUIRES_NEW时和为其他值时有不同的结果
- 方法A和方法B不在同一个类下,@transcation只用在方法B上,A方法内调用B方法,B的事务正常,B报错会回滚B方法的操作,但是如果A在调用B之前已经对数据库进行的操作不会执行回滚
- 开启代理(@EnableAspectJAutoProxy(exposeProxy = true)),并暴露代理器(AopContext.currentProxy())能使第3点的方法B的事务生效
- 方法的访问权限除了public外,其他权限都会使得@transcation事务失效
- Transaction rolled back because it has been marked as rollback-only(事务已回滚,因为它已标记为仅回滚) 问题
场景一:
- @transcation只在使用方法A上,A内无调用其他方法,事务正常,报错了则回滚数据库的操作,数据库并未有脏数据的产生
场景二:
- 方法A和方法B在同一个类下,@transcation都用在方法A和方法B上,A方法内调用B方法,A的事务正常,B的事务失效,且B的报错会导致A的事务进行回滚,即A和B都在A的事务中
可以看到方法A正常插入数据库,而方法B的报错执行事务的回滚也会导致A方法之前的插入操作回滚,这是因为在A方法类调用B方法是通过this.xxx()调用而不是经过代理调用,是无法对方法B的事务生效的,相当于了A方法和B方法都在A方法的事务中,这个原理可以参考下面的文章,比较详细的解析了
场景三:
- 方法A和方法B在同一个类下,@transcation只用在方法B上,A方法内调用B方法,B的事务失效,即使B方法报错也不会进行回滚,即B在报错前产生的脏数据会录入数据库
可以看到方法B报错了但是第一条的数据还是入库了,而且也没有影响到方法A的数据操作,就是相当于方法B事务失效,和完全没有写事务的情况一样,这个原因还是和上面场景二的一样,方法内调用使用的是this.xxx()
场景四:
- 方法A和方法B不在同一个类下,@transcation都用在方法A和方法B上,A方法内调用B方法,会因为B方法的@transcation设置的事务传播性的值不同而不同
B方法的传播行为值为Propagation.REQUIRES_NEW时,第一种情况报错在B方法
可以看到两个方法的数据都没有入库,方法B的方法报错事务进行了回滚也会影响到方法A,是因为调用方法B报错了会返回告诉方法A发生错误,因为方法B也属于方法A内中的一部分,只要报错A事务就会执行回滚
B方法的传播行为值为Propagation.REQUIRES_NEW时,第二种情况,报错在A方法
可以看出A方法报错进行了事务的回滚也不会影响到B方法,说明这两个方法处于不同的两个事务之中,Propagation.REQUIRES_NEW事务的传播行为相关,这个具体看参考文章,比较详细
报错在A方法,事务传播行为为其他值时,**Propagation.【REQUIRED|SUPPORTS|NESTED|MANDATORY】**时,方法B的事务失效,这个和事务的传播行为有关,相关解析请查看参考文章,比较详细
场景五:
- 方法A和方法B不在同一个类下,@transcation只用在方法B上,A方法内调用B方法,B的事务正常,B报错会回滚B方法的操作,但是如果A在调用B之前已经对数据库进行的操作不会执行回滚
报错在B方法,B事务生效,B事务进行回滚,但不会对A方法调用B方法之前的操作进行回滚,即事务B只会对方法B生效。
为什么这个时候B方法的事务会生效?因为方法B不在和方法A同一个类中,方法A调用方法B是通过spring代理调用的,会被事务拦截到,具体解析请看参考文章
报错在A方法,B事务一样生效,效果和普通无事务的效果一样,B方法的值一样入库成功,但是注意B的事务是生效的
场景六:
- 开启代理(@EnableAspectJAutoProxy(exposeProxy = true)),并暴露代理器(AopContext.currentProxy())能使第3点的方法B的事务生效(A和B在同一个类,A方法无事务,B方法有事务)
报错发生在方法B中,B事务生效,B事务进行回滚,不会对方法A调用方法B前的操作进行回滚,可以看到B事务是独立开的
报错发生在方法A中,B事务生效,数据一样能写入库中
场景七:
- 方法的访问权限除了public外,其他权限都会使得@transcation事务失效。使用不同类中A方法无事务,B方法事务为new进行校验。
- @Transactional 注解只能应用到 public 修饰的方法,因为是用cglib进行代理的,只能public,不然cglib无法进行代理,被aop增强的方法都应该是public的。(官网)
场景八:
- Transaction rolled back because it has been marked as rollback-only
- 方法B抛出异常,B是需要回滚的,方法Atry住希望A不进行回滚,但是会发现下面方法A还是执行了回滚。因为@Transactional
默认值是REQUIRED,如果当前存在事务,则加入该事务,所以B和A是同一个事务。当B方法throw异常的时候,就会标志事务状态为rollback-only,但是A和B又是在同一个事务中,所以当A执行完进行commit时会发现事务状态为rollback-only,就会报错回滚。想不让方法A执行回滚,可以在方法B中使用事务传播行为REQUIRES_NEW,这样事务B和事务A分开,就不会出现这种问题。
方法A回滚
方法A不会滚
@Transactional 事务传播可选值
REQUIRED(默认值):如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。required(必须的)。
SUPPORTS:如果当前存在事务,则加入该事务;如果当前不存在事务,则以非事务方式继续运行。supports(支持的)
MANDATORY:如果当前存在事务,则加入该事务;如果当前不存在事务,则抛出异常。mandatory(强制的)
REQUIRES_NEW:重新创建一个新的事务,如果当前存在事务,暂停当前的事务。requires_new(依赖新的)
NOT_SUPPORTED:以非事务的方式运行,如果当前存在事务,暂停当前的事务。not_supported(不支持的)
NEVER:以非事务的方式运行,如果当前存在事务,则抛出异常。never(从不)
NESTED:和 REQUIRED 效果一样。nested(嵌套)
@Transactional 事务隔离级别
DEFAULT(默认值):使用底层数据库默认的隔离级别。
READ_UNCOMMITTED:未提交读。无法防止。
READ_COMMITTED:可提交读。可以防止脏读。(RC)
REPEATABLE_READ:可重复读。可以防止脏读、可重复读。mysql 默认隔离级别。(RR)
SERIALIZABLE:串行事务。最高事务隔离级别,可以防止脏读、可重复读、幻读。
事务的执行原理、传播行为、事务隔离等级、相关的失效问题原因请参考下面的参考文章,比较详细
参考文章:
https://blog.csdn.net/jiaxiaoyan321/article/details/126752355
https://blog.csdn.net/wangmx1993328/article/details/89644934