spring mysql 事务回滚失败_技术贴 | Spring事务不回滚问题排查

原标题:技术贴 | Spring事务不回滚问题排查

99585dbe5bed4c975c9af2d3e026fdaa.png

本技术贴是我司技术小哥在开发新项目中遇到的问题时的分析及思考,学霸的人生学无止境。

01

问题描述

在新项目的开发中遇到一个奇葩的问题:方法添加了事务后数据库不回滚,而且不是每一个方法都不回滚,是有的方法回滚,有的不回滚。

02

问题背景

新项目使用SpringBoot2.0+Mybatis框架,启用事务的方式是在类的方法上使用@Transactional注解的方式来启用事务的

(旧项目直接在类上面加@Transactional,该类下所有方法都支持事务,考虑到一些查询方法是不需要事务的,这样会影响性能,所以改为在方法上添加@Transactional注解,所以旧项目事务没有出现问题)。

03

排查问题

经过对比发现:

回滚的方法都是由controller直接调用有@Transactional注解的,而不回滚的方法都是由controller调用了方法A(该方法没有声明事务),再由方法A调用方法B(该方法声明了事务)

例如:测试类TestController

cd2421cc63be2586c85fd40a7e2a4bf2.png

TestOneServiceImpl

dbe36618a7cc8820a09341f4fcea3102.png

TestController类中的test()方法调用了

TestOneServiceImpl中的testFunctionOne() 方法

testFunctionOne()方法调用了 testFunctionTwo()方法(启用了事务) 。

如果事务起作用,则user对象中userName字段不会更改

首先查询userName值

d0239b020a2b7dd54cc621440a5ba160.png

调用 test()方法 查询数据库

94ac3551d9aa2ddec74fc6ccbbcf0980.png

发现userName值已被更改 ,testFunctionTwo()方法中的事务没起作用

在test()方法中直接调用testFunctionTwo()方法

94bd62d10947b31234bbc57b0ffca63b.png

调用前userName值

5faa7f2c7a3d9ded118bec3f3ce3d40e.png

调用后userName值

c1ac8d00bd9b3313a4b34a5f3c52aa56.png

发现userName值未被更改 ,testFunctionTwo()方法中的事务起作用了,数据库回滚,复现了项目中的问题。

经过查询spring官网 发现原文

In proxy mode (which is the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation, in effect, a method within the target object calling another method of the target object, will not lead to an actual transaction at runtime even if the invoked method is marked with @Transactional. Also, the proxy must be fully initialized to provide the expected behaviour so you should not rely on this feature in your initialization code, i.e. @PostConstruct.

大体意思是 默认的代理模式下,只有被代理拦截的外部方法。这意味着,自调用即类内部方法调用本类内部的其他方法并不会引起事务行为(即使该方法被@Transactional注解标记)。

由上可见,新项目中事务不回滚的原因已被找到。

04

解决方案

无脑方式:

将需要事务的方法提到另一个类中

官方解决方案:

使用AspectJ[JL1] 代理模式替代默认代理(暂未实现)

通过初始化方法在目标对象中注入代理对象

0caa9366fa88b816ca584a170fe8516e.png

调用前userName值

c53d9e52110547cdff7bb90bd04b73fc.png

调用后userName值

a9e0b52552a7712afe06a3eca6b5b410.png

小结

经过这次事件 认真学习了一下spring事务的注意事项总结如下:

默认代理模式下 只有public修饰的方法事务才起作用, private protected 包内可见方法即使用@Transactional声明了事务,不会出错 也不会起作用(可以使用AspectJ代理来解决)

任何 RuntimeException 抛出时触发回滚, 检查异常抛出时不会回滚。如果想实现抛出检查异常回滚需在@Transactional中添加属性rollbackFor 指定需要回滚的异常类 norollbackFor 自定义不回滚异常

默认的代理模式下,只有被代理拦截的外部方法才事务才回起作用。自调用即类内部方法调用本类内部的其他方法并不会引起事务行为(即使该方法被@Transactional注解标记)。 如果想实现自调用 需更换代理模式 使用AspectJ代理来解决

异常如果被catch住了并且没有抛出 事务是不会起作用的

感谢李斌对此次故障排查做出的贡献

参考文献:Spring官方文档

https://docs.spring.io/spring/docs/4.3.13.RELEASE/spring-framework-reference/htmlsingle/#transaction-declarative-annotations返回搜狐,查看更多

责任编辑:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值