Spring 事务方法与非事务方法相互调用 @Transactional 注解失效分析

18 篇文章 1 订阅

写这篇文章的初衷是因为在实用Spring事务的时候,我在A方法中调用了B方法和C方法,原意是想如果B方法中报错,则回滚B方法;如果C方法中异常,则只回滚C方法,B方法和C方法不会相互影响。于是我在B方法和C方法上加了@Transactional注解,A没有加。结果测试发现结果并非所想。于是便分析了一番,现将分析结果做个分享:

1、spring事务的传播机制及原因分析

要知道上面原因产生的原因,则首先得知道Spring事务的传播机制

PROPAGATION_REQUIRED – 支持当前事务,如果当前没有事务,就新建一个事务。
这是最常见的选择。
PROPAGATION_SUPPORTS – 支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY – 支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW – 新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED – 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER – 以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED – 如果当前存在事务,则在嵌套事务内执行。
如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。

spring默认的是PROPAGATION_REQUIRED机制,如果方法A标注了注解@Transactional 是完全没问题的,执行的时候传播给方法B,因为方法A开启了事务,线程内的connection的属性autoCommit=false,并且执行到方法B时。事务传播依然是生效的,得到的还是方法A的connection,autoCommit还是为false,所以事务生效;反之,如果方法A没有注解@Transactional 时是不受事务管理的,autoCommit=true,那么传播给方法B的也为true,执行完自动提交,即使B标注了@Transactional ;

在一个Service内部,事务方法之间的嵌套调用,普通方法和事务方法之间的嵌套调用,都不会开启新的事务。是因为spring采用动态代理机制来实现事务控制,而动态代理最终都是要调用原始对象的,而原始对象在去调用方法时,是不会再触发代理了!
所以以上就是为什么我在没有标注事务注解的方法A里去调用标注有事务注解的方法B而没有事务滚回的原因。
看到这里有的人可能回想,那我直接在方法A上加个注解不就行了么,为何要么麻烦?
首先由于我这里是需要循环更新提交的,而B方法和C方法没有直接的关联关系。但是都和A有关系,B失败,并不想影响C方法的执行。
因此,显然上面所说的在A方法上加上注解的方式行不通。

解决方案

1、把方法B、C抽离到另外一个XXService中去,并且在这个Service中注入XXService,使用XXService调用方法B和C。

这种方法的缺点是:一点也不优雅,且要产生很多冗余文件,看起来很烦,实际开发中也几乎没人这么做吧?反正我不建议采用此方案;

2、通过在方法内部获得当前类代理对象的方式,通过代理对象调用方法B和C

动态代理最终都是要调用原始对象的,而原始对象在去调用方法时,是不会再触发代理了!所以我们就使用代理对象来调用,就会触发事务;
综上解决方案,我觉得第二种方式简直方便到炸,那怎么获取代理对象呢?这里提供两种方式:
1、使用 ApplicationContext 上下文对象获取该对象;
2、使用 AopContext.currentProxy() 获取代理对象,但是需要配置exposeProxy=true
第一种大家相比都已经很清楚了,下面我们来看一下第二种的实现方式:

  • springboot启动类加上注解:@EnableAspectJAutoProxy(exposeProxy = true)
    在这里插入图片描述
  • 方法内部获取代理对象调用方法
    在这里插入图片描述
    完了后再测试,数据顺利回滚。希望以上方法对各位有所帮助。
  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在一个有@Transactional注解方法调用处再手动开启事务是不必要的,因为@Transactional注解已经为该方法提供了事务管理功能。当方法调用时,Spring框架会自动为该方法开启一个事务,并在方法执行完毕后根据方法的执行结果决定是提交事务还是回滚事务。因此,在这种情况下,手动开启事务是多余的,并且可能导致事务管理的混乱。所以,不建议在有@Transactional注解方法调用处再手动开启事务。\[1\]\[3\] #### 引用[.reference_title] - *1* [@Transactional注解管理事务Spring手动提交事务(二)](https://blog.csdn.net/qq_45297578/article/details/117282911)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [解决@Transactional在子方法失效的问题,解决@Transactional失效问题,Java用Spring手动开启事务](https://blog.csdn.net/weixin_43620015/article/details/125759593)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [@Transactional注解超详细](https://blog.csdn.net/weixin_45490198/article/details/131320526)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Calvin_it

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值