springboot下@transcation使用基本介绍

springboot下@transcation基本使用的几种可能

普通常使用的几种可能(事务的传播行为默认值Propagation.REQUIRED):

  1. @transcation只在使用方法A上,A内无调用其他方法,事务正常
  2. 方法A和方法B在同一个类下,@transcation都用在方法A和方法B上,A方法内调用B方法,A的事务正常,B的事务失效,且B的报错会导致A的事务进行回滚,即A和B都在A的事务中
  3. 方法A和方法B在同一个类下,@transcation只用在方法B上,A方法内调用B方法,B的事务失效,即使B方法报错也不会进行回滚,即B在报错前产生的脏数据会录入数据库
  4. 方法A和方法B不在同一个类下,@transcation都用在方法A和方法B上,A方法内调用B方法,会因为B方法的@transcation设置的事务传播性的值不同而不同,B方法的传播行为值为Propagation.REQUIRES_NEW时和为其他值时有不同的结果
  5. 方法A和方法B不在同一个类下,@transcation只用在方法B上,A方法内调用B方法,B的事务正常,B报错会回滚B方法的操作,但是如果A在调用B之前已经对数据库进行的操作不会执行回滚
  6. 开启代理(@EnableAspectJAutoProxy(exposeProxy = true)),并暴露代理器(AopContext.currentProxy())能使第3点的方法B的事务生效
  7. 方法的访问权限除了public外,其他权限都会使得@transcation事务失效
  8. Transaction rolled back because it has been marked as rollback-only(事务已回滚,因为它已标记为仅回滚) 问题
  9. 如果使用@Transactional,会使多数据源失效
  10. 调用的方法被final声明或者static声明,无法继承父类与父类的方法
  11. 手动抛出的异常不符合,rollbackFor错误的指定,异常被trycatch
  12. 多线程的情况
  13. 数据库本身不支持事务

场景一:

  • @transcation只在使用方法A上,A内无调用其他方法,事务正常,报错了则回滚数据库的操作,数据库并未有脏数据的产生

springboot下@transcation使用基本介绍_第1张图片


场景二:

  • 方法A和方法B在同一个类下,@transcation都用在方法A和方法B上,A方法内调用B方法,A的事务正常,B的事务失效,且B的报错会导致A的事务进行回滚,即A和B都在A的事务中

springboot下@transcation使用基本介绍_第2张图片

可以看到方法A正常插入数据库,而方法B的报错执行事务的回滚也会导致A方法之前的插入操作回滚,这是因为在A方法类调用B方法是通过this.xxx()调用而不是经过代理调用,是无法对方法B的事务生效的,相当于了A方法和B方法都在A方法的事务中,这个原理可以参考下面的文章,比较详细的解析了


场景三:

  • 方法A和方法B在同一个类下,@transcation只用在方法B上,A方法内调用B方法,B的事务失效,即使B方法报错也不会进行回滚,即B在报错前产生的脏数据会录入数据库

springboot下@transcation使用基本介绍_第3张图片

可以看到方法B报错了但是第一条的数据还是入库了,而且也没有影响到方法A的数据操作,就是相当于方法B事务失效,和完全没有写事务的情况一样,这个原因还是和上面场景二的一样,方法内调用使用的是this.xxx()


场景四:

  • 方法A和方法B不在同一个类下,@transcation都用在方法A和方法B上,A方法内调用B方法,会因为B方法的@transcation设置的事务传播性的值不同而不同

B方法的传播行为值为Propagation.REQUIRES_NEW时,第一种情况报错在B方法

springboot下@transcation使用基本介绍_第4张图片

springboot下@transcation使用基本介绍_第5张图片

可以看到两个方法的数据都没有入库,方法B的方法报错事务进行了回滚也会影响到方法A,是因为调用方法B报错了会返回告诉方法A发生错误,因为方法B也属于方法A内中的一部分,只要报错A事务就会执行回滚


B方法的传播行为值为Propagation.REQUIRES_NEW时,第二种情况,报错在A方法

springboot下@transcation使用基本介绍_第6张图片

springboot下@transcation使用基本介绍_第7张图片

可以看出A方法报错进行了事务的回滚也不会影响到B方法,说明这两个方法处于不同的两个事务之中,Propagation.REQUIRES_NEW事务的传播行为相关,这个具体看参考文章,比较详细


报错在A方法,事务传播行为为其他值时,**Propagation.【REQUIRED|SUPPORTS|NESTED|MANDATORY】**时,方法B的事务失效,这个和事务的传播行为有关,相关解析请查看参考文章,比较详细

springboot下@transcation使用基本介绍_第8张图片

springboot下@transcation使用基本介绍_第9张图片


场景五:

  • 方法A和方法B不在同一个类下,@transcation只用在方法B上,A方法内调用B方法,B的事务正常,B报错会回滚B方法的操作,但是如果A在调用B之前已经对数据库进行的操作不会执行回滚

报错在B方法,B事务生效,B事务进行回滚,但不会对A方法调用B方法之前的操作进行回滚,即事务B只会对方法B生效。
为什么这个时候B方法的事务会生效?因为方法B不在和方法A同一个类中,方法A调用方法B是通过spring代理调用的,会被事务拦截到,具体解析请看参考文章

springboot下@transcation使用基本介绍_第10张图片

springboot下@transcation使用基本介绍_第11张图片


报错在A方法,B事务一样生效,效果和普通无事务的效果一样,B方法的值一样入库成功,但是注意B的事务是生效的

springboot下@transcation使用基本介绍_第12张图片

springboot下@transcation使用基本介绍_第13张图片

场景六:

  • 开启代理(@EnableAspectJAutoProxy(exposeProxy = true)),并暴露代理器(AopContext.currentProxy())能使第3点的方法B的事务生效(A和B在同一个类,A方法无事务,B方法有事务)

报错发生在方法B中,B事务生效,B事务进行回滚,不会对方法A调用方法B前的操作进行回滚,可以看到B事务是独立开的

springboot下@transcation使用基本介绍_第14张图片

报错发生在方法A中,B事务生效,数据一样能写入库中

springboot下@transcation使用基本介绍_第15张图片


场景七:

  • 方法的访问权限除了public外,其他权限都会使得@transcation事务失效。使用不同类中A方法无事务,B方法事务为new进行校验。
  • @Transactional 注解只能应用到 public 修饰的方法,因为是用cglib进行代理的,只能public,不然cglib无法进行代理,被aop增强的方法都应该是public的。(官网)

springboot下@transcation使用基本介绍_第16张图片

springboot下@transcation使用基本介绍_第17张图片
springboot下@transcation使用基本介绍_第18张图片
源码:不支持非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回滚

springboot下@transcation使用基本介绍_第19张图片
在这里插入图片描述

方法A不会滚

springboot下@transcation使用基本介绍_第20张图片

场景九

如果使用@Transactional,会使多数据源失效,方法A使用了事务,调用了其他数据源的方法B,这时候会导致方法B使用的数据源是方法A的,导致出错。
使用多数据源的时候请使用分布式事务,列如seata组件
springboot下@transcation使用基本介绍_第21张图片
springboot下@transcation使用基本介绍_第22张图片

场景十

调用的方法被final声明或者static声明,无法继承父类与父类的方法。
spring事务底层使用了aop,也就是通过jdk动态代理或者cglib,帮我们生成了代理类,在代理类中实现的事务功能。但如果某个方法用final修饰了,那么在它的代理类中,就无法重写该方法,而添加事务功能。
static一样会导致代理失效

场景十一

手动抛出的异常不符合,rollbackFor错误的指定,异常被trycatch

默认情况下事务仅回滚运行时异常和Error
我们可以通过指定@Transactional(rollbackFor = Exception.class)的方式进行全异常捕获。
如果我们手动抛出的异常又不属于rollbackFor指定的异常,是不会执行回滚的。
try/catch后不手动抛出异常,则事务无法获取异常信息,认为无异常出现不会回滚。

场景十二

多线程的情况 //todo

场景十三

数据库本身不支持事务
Mysql的Myisam存储引擎是不支持事务的,只有innodb存储引擎才支持。

@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
https://blog.csdn.net/mccand1234/article/details/124571619

你可能感兴趣的:(java,spring,boot,java,数据库)