@Transactional调用不生效问题及解决方案

@Transactional调用不生效问题及解决方案

参考博文:
@Transactional的几种失效场景_勇往直前的小白的博客-CSDN博客
Spring事务回滚和异常类_huapro.vip的博客-CSDN博客
感谢以上博主的分享,致敬!

1、事务是什么


事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消.也就是事务具有原子性,一个事务中的一系列的操作要么全部都成功,要么一个都不做.

2、事务的四大特性


--原子性:整个事务中的所有操作,要么全部完成,要么全部都不完成,不可能停滞在中间某个环节.事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样.      
--一致性:在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏.      
--隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致.事务隔离分为不同级别,包括读未提交(Read uncommitted)、读已提交(read committed)、可重复读(repeatable read)和串行化(Serializable).
--持久性:在事务完成以后,该事务对数据库所作的更改便持久的保存在数据库之中,并不会被回滚.

3、@Transactional


@Transactional是spring中常用的注解之一,通常情况下我们在需要对一个service方法添加事务时,加上这个注解,如果发生unchecked exception(运行时异常),就会发生rollback.

--作用
@Transactional 可以作用在接口、类、类方法:
作用于类: 当把@Transactional 注解放在类上时,表示所有该类的public方法都配置相同的事务属性信息
作用于方法: 当类配置了@Transactional,方法也配置了@Transactional,方法的事务会覆盖类的事务配置信息
作用于接口: 不推荐这种使用方法,因为一旦标注在Interface上并且配置了Spring AOP 使用CGLib动态代理,将会导致@Transactional注解失效

--属性
@Transactional注有如下属性:
propagation: 代表事务的传播行为
isolation : 事务的隔离级别
timeout : 事务的超时时间,默认值为 -1.如果超过该时间限制但事务还没有完成,则自动回滚事务
readOnly : 指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true
noRollbackFor: 抛出指定的异常类型,不回滚事务,也可以指定多个异常类型

4、@Transactional失效场景

1、同一个类中方法调用(同一个类中,方法B有事务,方法A没有,在该类中方法A调用方法B,事务不生效)
2、@Transactional 应用在非 public 修饰的方法上
3、异常捕获后,没有抛出runtimeException异常
(事务是否执行取决于是否抛出runtime异常。如果抛出runtime exception 并在你的业务方法中没有catch到的话,事务会回滚。如果一定要写try-catch,需要在catch最后一行throw一个runtimeException,这样事务才会执行)
例如:
catch(ParseException e){
   throw new RuntimeException("抛出异常");
}
4、数据库引擎不支持事务。(仅InnoDB支持)
5、默认情况下,Spring会对Error或者RuntimeException异常进行事务回滚,
   其他继承自java.lang.Exception的异常:如IOException、TimeoutException等,不会回滚(如下图,绿色的是Spring会回滚的)
6、@Transactional 注解属性 propagation 设置错误,一定要设置成支持事务的,或者不设置(默认是Propagation.REQUIRED)

--同一个类中方法调用失效原因分析
Spring采用动态代理(AOP)实现对bean的管理和切片,它为我们的每个class生成一个代理对象.只有在代理对象之间进行调用时,可以触发切面逻辑。而在同一个class中,方法B调用方法A,调用的是原对象的方法,而不通过代理对象.所以Spring无法切到这次调用,也就无法通过注解保证事务性了。
也就是说,在同一个类中的方法调用,则不会被方法拦截器拦截到,因此事务不会起作用

请添加图片描述

5、解决方法

方法1、自己@Autowired自己
问题:可能会出现循环依赖的问题

方法2、将事务方法放到另一个类中(或者单独开启一层,取名“事务层”)进行调用,即符合了在对象之间调用的条件

方法3、获取本对象的代理对象,再进行调用

启动类中添加 @EnableAspectJAutoProxy(exposeProxy = true)
请添加图片描述

代码中使用AopContext.currentProxy()获取到当前代理类,强行通过代理类,激活事务切面
请添加图片描述

exposeProxy = true用于控制AOP框架公开代理,公开后才可以通过AopContext获取到当前代理类(默认情况下不会公开代理,因为会降低性能)
使用@EnableAspectJAutoProxy注解开启Spring对AspectJ的支持,表示开启AOP代理自动配置,表示使用cglib进行代理对象的生成

更多资料

@Transactional事务不生效的几种解决方案 - 知乎

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值