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,则事务会 ...
 
随机推荐
- CentOS 7安装Nginx 1.10.2
			
安装epel-release源并进行安装 yum install epel-release yum update(时间会有点长) yum install nginx 相关操作: systemctl s ...
 - Activity的常用控件
			
TimerPick(时间控件)public Integer getCurrentHour() //返回当前设置的小时public Integer getCurrentMinute()//返回当前设置的 ...
 - 用Python爬取B站、腾讯视频、爱奇艺和芒果TV视频弹幕!
			
众所周知,弹幕,即在网络上观看视频时弹出的评论性字幕.不知道大家看视频的时候会不会点开弹幕,于我而言,弹幕是视频内容的良好补充,是一个组织良好的评论序列.通过分析弹幕,我们可以快速洞察广大观众对于视频 ...
 - shell-的bash内部命令变量介绍与shift等
			
一:shell的bash内部命令变量介绍与shift等 1. bash内部变量 有些内部命令在目录列表时是看不见的,他们有shell本身提供,常用的内部命令有:echo,eval,exec,e ...
 - 正式班D7
			
2020.10.13星期二 正式班D7 一.上节课复习 Linux发展 批处理系统 多道技术 分时操作系统 multics->Unix->minix->Linux(如Redhat.c ...
 - 【全网免费VIP观看】哔哩哔哩番剧解锁大会员-集合了优酷-爱奇艺-腾讯-芒果-乐视-ab站等全网vip视频免费破解去广告-高清普清电视观看-持续更新
			
哔哩哔哩番剧解锁大会员-集合了优酷-爱奇艺-腾讯-芒果-乐视-ab站等全网vip视频免费破解去广告-高清普清电视观看-持续更新 前言 突然想看电视,结果 没有VIP 又不想花钱,这免费的不久来啦. 示 ...
 - HCIA——应用层常用协议
			
DNS协议 1.什么是DNS协议呢? DNS协议简单来说就是为IP取一个别名的系统(叫域名如www.baidu.com),最终目的是便于我们记忆. 一个域名可能有多个IP,同样一个IP可能也会有多个域 ...
 - centos7安装kafka 转
			
CentOS7安装和使用kafka 环境准备 安装kafka之前我们需要做一些环境的准备 1.centOS7系统环境 2.jdk环境 3.可用的zookeeper集群服务 安装jdk ...
 - electron-updater实现更新electron应用程序
			
electron-updater实现更新electron应用程序 第一步 安装"electron-updater": "^4.3.5", 打开package.j ...
 - C# / VB.NET 在PPT中创建、编辑PPT SmartArt图形
			
本文介绍通过C#和VB.NET程序代码来创建和编辑PPT文档中的SmartArt图形.文中将分两个操作示例来演示创建和编辑结果. 使用工具:Spire.Presentation for .NET ho ...