Spring事务失效

Spring 事务的分类
Spring 提供了两种事务管理方式:声明式事务管理和编程式事务管理。对不同的持久层访问技术,编程式事务提供一致的事务编程风格,通过模板化的操作一致性地管理事务;而声明式事务基于 Spring AOP 实现,却并不需要程序开发者成为 AOP 专家,亦可轻易使用 Spring 的声明式事务管理。

声明式事务
Spring 的声明式事务管理是建立在 Spring AOP 机制之上的,其本质是对目标方法前后进行拦截,并在目标方法开始之前创建或者加入一个事务,在执行完目标方法之后根据执行情况提交或者回滚事务。

简单地说,声明式事务是编程式事务 + AOP 技术包装,使用注解进行扫包,指定范围进行事务管理。声明式事务管理要优于编程式事务管理,这正是 Spring 倡导的非侵入式的开发方式

编程式事务
在 Spring 出现以前,编程式事务管理对基于 POJO 的应用来说是唯一选择。我们需要在代码中显式调用 beginTransaction()、commit()、rollback() 等事务管理相关的方法,这就是编程式事务管理。

简单地说,编程式事务就是在代码中显式调用开启事务、提交事务、回滚事务的相关方法。

Spring 事务的原理
使用 AOP 环绕通知 和 异常通知。

事务控制也可在环绕通知中手动在捕捉异常部分添加回滚 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

注意: 在使用 Spring 事务时不能使用 try-catch 进行异常捕获,要将异常抛给外层,使其进行异常拦截,触发事务机制。

事务的传播行为 所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。

REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值。

REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起。

SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。

NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起。

NEVER:以非事务方式运行,如果当前存在事务,则抛出异常。

MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。

NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于 REQUIRED。

基于注解 @Transactional 声明事务失效分析
在开发过程中,可能会遇到使用 @Transactional 进行事务管理时出现失效的情况。

常见失效场景

  1. 如果使用 MySQL 且引擎是 MyISAM,则事务会不起作用,原因是 MyISAM 不支持事务,改成 InnoDB 引擎则支持事务。

  2. 注解 @Trasactional 只能加在 public 修饰的方法上事务才起效。如果加在 protect、private 等非 public 修饰的方法上,事务将失效。

  3. 如果在开启了事务的方法内,使用了 try-catch 语句块对异常进行了捕获,而没有将异常抛到外层,事务将不起效。

  4. 简单地说,当存在方法之间调用时,异常发生在无事务的方法中,但不是被调用的方法产生的,被调用的方法的事务无效。只有异常发生在开启事务的方法内,事务才有效。

如果 A 方法开启了事务,B 方法没有开启事务,B 方法调用了 A 方法。如果 B 方法发生异常中,但不是调用的 A
方法产生的,则异常不会使 A 方法的事务回滚,此时事务无效。如果 B 方法发生异常中,异常是调用的 A 方法产生的,则 A
方法的事务回滚,此时事务有效。在 B 方法上加上注解 @Trasactional,这样 A 和 B
方法就在同一个事务里了,不管异常产生在哪里,事务都是有效的。

  1. 如果使用了Spring + MVC,则 context:component-scan 重复扫描问题可能会引起事务失效。

原因分析

在应用系统调用声明 @Transactional 的目标方法时,Spring Framework 默认使用 AOP 代理,在代码运行时生成一个代理对象,再由这个代理对象来统一管理。Spring 事务是使用 AOP 环绕通知和异常通知,就是对方法进行拦截,在方法执行前开启事务,在捕获到异常时进行事务回滚,在方法执行完成后提交事务。

最后
Spring 团队建议在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。在接口上使用 @Transactional 注解,只能当你设置了基于接口的代理时它才生效。因为注解是不能继承的,这就意味着如果正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装。

Spring 文档中写到:Spring AOP 部分使用 JDK 动态代理或者 CGLIB 来为目标对象创建代理,如果被代理的目标对象实现了至少一个接口,则会使用 JDK 动态代理。所有该目标类型实现的接口都将被代理。若该目标对象没有实现任何接口,则创建一个CGLIB代理。

参考
https://juejin.im/post/5b00c52ef265da0b95276091#heading-9

https://blog.csdn.net/rylan11/article/details/76609643

https://blog.csdn.net/justloveyou_/article/details/73733278

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值