Spring 自调用事务失效,你是怎么解决的?
前言
相信大家都遇到一种事务失效场景,那就是 Spring 自调用,就是在 Service 方法内,调用另一个加
@Transactional注解的方法,发现事务失效,这时候你是怎么解决的呢?公众号:『 刘志航 』,记录工作学习中的技术、开发及源码笔记;时不时分享一些生活中的见闻感悟。欢迎大佬来指导!
事情回顾
那是一个我忘了天气咋样的下午,突然蹦出一个小红点,嗯~ 挺着急的小红点。

原来是事务失效了!
莫慌!莫慌!



最后小伙伴选择了抽走,是我的工具类不香了么?

当然故事的结果是完美的,问题解决了。

事务
在开发中涉及到同时操作多个表的时候,要保证两个操作要么一起成功,要么一起失败,这时候就需要用到事务。
现在一般使用的都是基于 @Transactional 注解的声明式事务。
而事务使用过程中有以下几个注意事项:
- 事务只能应用到 public 方法上才会有效;
- 事务需要从外部调用,Spring 自调用会失效;
- 建议事务注解 @Transactional 一般添加在实现类上。
当然这几句话不是说我的,人家官方文档可是明确说明的!

这里可是说明了应仅将 @Transactional 注解应用于具有公开可见性的方法。如果对受 protected, private o或 package-visible 修饰的方法使用,则不会引发任何错误,但是被注解的方法不会显示已配置的事务设置。
说白了,就是你用了,不会报错,但是不生效!

至于建议加在实现类上,这个只是建议,不过如果加在接口类或接口方法上时,只有配置基于接口的代理才会生效。所以这块还是老老实实的加在实现类或实现类方法上吧。

因为代理模式只拦截通过代理传入的外部方法调用,所以自调用事务是不生效的。
官方的解释还是比较简单明了的,虽然我看不懂,但是不影响我截图。
那我还是再截一个吧……

实际使用
但是在开发中,小伙伴们往往会遇到这种情况!

本来自己写的代码就一坨坨的又臭又长,里面有各种验签、验参、查询、验证等等,就想着来个事务,让事务包裹的范围最小,仅仅在同时更新的时候加上事务吧!

这么写,咦~ IDEA 报错了,好像不能 private 修饰,那我改成 public。
很显然事务是不生效的。
把更新的代码放到又臭又长的代码里面,让它变得更臭更长,然后用 @Transactional 注解一加。完美解决!

请放过那坨代码吧!来看看下面的办法。
解决方案 1

那我改成外部调用不就行了么?
再声明一个 Service,把更新表的逻辑放过去。
我一般就喜欢使用这个办法。
解决方案 2
使用编程式事务,前面说了,使用声明式事务时,又这又那,我换一种总可以吧!

你看,我还把方法改成 private 修饰了,事务也生效。完美解决!

其实这个方法也很不错哦!
解决方案 3
又想用注解,又想自调用怎么办?

不过... 麻烦一点还是可以的。
咱们可以参考编程式事务的方式,不就是不让自调用么,我调外部方法,然后外部方法再给我调回来不就可以了。
@Component
public class TransactionalComponent {
public interface Cell {
void run() throws Exception;
}
@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
public void required(Cell cell) throws Exception {
cell.run();
}
}
这样的话不就可以通过 TransactionalComponent 调用了么,并且还可以使用 lambda 表达式。

当然基于这个版本也可以做一个迭代,就是使用静态方法调用,不用每次都用 @Autowired 注入一次。
public class TransactionalUtils {
private static volatile TransactionalComponent transactionalComponent;
private static synchronized TransactionalComponent getTransactionalComponent() {
if (transactionalComponent == null) {
// 从容器中获取 transactionalComponent
transactionalComponent = ApplicationContextUtils.getBean(TransactionalComponent.class);
}
return transactionalComponent;
}
public static void required(TransactionalComponent.Cell cell) throws Exception {
getTransactionalComponent().required(cell);
}
}

这样通过工具类 TransactionalUtils 便可以直接调用静态方法的方式执行事务操作。
总结
结束语
本文主要介绍为什么会遇到事务失效,以及事务失效的避免方式,同时提供了三种方式来解决自调用事务失效的问题。不足之处,欢迎指正。
相关资料
Spring 自调用事务失效,你是怎么解决的?的更多相关文章
- Spring,SpringMvc配置常见的坑,注解的使用注意事项,applicationContext.xml和spring.mvc.xml配置注意事项,spring中的事务失效,事务不回滚原因
1.Spring中的applicationContext.xml配置错误导致的异常 异常信息: org.apache.ibatis.binding.BindingException: Invalid ...
- spring+springMVC,声明式事务失效,原因以及解决办法
http://blog.csdn.net/z69183787/article/details/37819627#comments 一.声明式事务配置: <bean id="transa ...
- Spring声明式事务如何选择代理方式?
Spring声明式事务如何选择代理方式 解决方法: 1.基于注解方法: <tx:annotation-driven transaction-manager="txManager&q ...
- Spring事务Transactional和动态代理(三)-事务失效的场景
系列文章索引: Spring事务Transactional和动态代理(一)-JDK代理实现 Spring事务Transactional和动态代理(二)-cglib动态代理 Spring事务Transa ...
- spring声明式事务 同一类内方法调用事务失效
只要避开Spring目前的AOP实现上的限制,要么都声明要事务,要么分开成两个类,要么直接在方法里使用编程式事务 [问题] Spring的声明式事务,我想就不用多介绍了吧,一句话“自从用了Spring ...
- Spring component-scan 的逻辑 、单例模式下多实例问题、事务失效
原创内容,转发请保留:http://www.cnblogs.com/iceJava/p/6930118.html,谢谢 之前遇到该问题,今天查看了下 spring 4.x 的代码 一,先理解下 con ...
- spring事务失效情况分析
详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt113 <!--[if !supportLists]-->一.&l ...
- spring声明式事务 同一类内方法调用事务失效(转)
原文 https://blog.csdn.net/jiesa/article/details/53438342 [问题] Spring的声明式事务,我想就不用多介绍了吧,一句话“自从用了Spring ...
- Spring事务失效的原因
http://blog.csdn.net/paincupid/article/details/51822599 Spring事务失效的原因 5种大的原因 如使用mysql且引擎是MyISAM,则事务会 ...
随机推荐
- 039 01 Android 零基础入门 01 Java基础语法 05 Java流程控制之循环结构 01 循环结构概述
039 01 Android 零基础入门 01 Java基础语法 05 Java流程控制之循环结构 01 循环结构概述 本文知识点:循环结构概述 循环结构主要内容 while 循环 do-whiile ...
- TTL和CMOS电平
参考: 1.https://baike.baidu.com/item/TTL%E7%94%B5%E5%B9%B3/5904345 2.https://baike.baidu.com/item/CMOS ...
- [学习笔记] 树上倍增求LCA
倍增这种东西,听起来挺高级,其实功能还没有线段树强大.线段树支持修改.查询,而倍增却不能支持修改,但是代码比线段树简单得多,而且当倍增这种思想被应用到树上时,它的价值就跟坐火箭一样,噌噌噌地往上涨. ...
- 萌新学python
python python安装 进入官网http://www.python.org/download/ 下载 我下的是3.6.6大家可以根据需要下载(3.x和2.x不兼容请小心) 之后安装就可以了 p ...
- Netty之网络编程数据编码
一.概况 我们在进行网络编程中会把各种数据转换为byte数据以便能在网络上传输,最常见的网络字节序--Little-Endian和Big-Endian,也让好多初进网络编程的新手摸不着头脑,还有按位或 ...
- postgreSQL与Kingbase 字符串裁剪区别
--postgreSQL postgres=# select substring('abcdefg',0,4); substring abc (1 行记录) postgres=# select sub ...
- day33 Pyhton logging 模块和包
一内容回顾 try/except try/except/else try/finally try/except try/except/finally try/except/else/finally 报 ...
- 加密sqlite3数据库文件
目录 EncryptSqlite3 实现原理 使用方法 不足之处 GitHub地址 EncryptSqlite3 加密sqlite3数据库,产生的数据库文件别人打不开. 实现原理 在写入文件前对每个字 ...
- pytest文档42-fixture参数化params
前言 参数化是自动化测试里面必须掌握的一个知识点,用过 unittest 框架的小伙伴都知道使用 ddt 来实现测试用例的参数化. pytest 测试用例里面对应的参数可以用 parametrize ...
- filebeat7.5 日志
百度网盘 提取码: 6cvu 解压 tar -zxvf filebeat-7.5.0-linux-x86_64.tar.gz mv filebeat-7.5.0-linux-x86_64 /usr/l ...