SpringBoot事务管理

文章探讨了在SpringBoot中使用@Transactional进行事务管理时遇到的问题,包括同一类内方法调用导致事务失效、异常被捕获后事务无法自动回滚、rollbackFor属性设置不当等。解决方案包括手动设置事务回滚、正确处理异常以及理解事务的传播机制。此外,文章还提到了多数据源配置下事务和数据源切换的冲突及其解决办法。
摘要由CSDN通过智能技术生成

场景:Excel导入人口信息。需要先删除重复的,然后循环插入一条条信息(due to 许多原因,整个流程最开始是放在一个方法里的)。由于导入的一条数据涉及多个表格的查询和插入,会存在需要回滚的情况。e.g. A数据先插入,B数据后插入。 由于B数据插入错误,A也必须回滚到插入前的状态。但一直没有成功。刚好对spring boot的事务回滚不是很了解,现在记录下学习的情况。

一 、事务的两种管理机制

a. 声明式事务

基于AOP面向切面的,它将具体业务与事务处理部分解耦,代码侵入性很低,所以在实际开发中声明式事务用的比较多。声明式事务也有两种实现方式,一是基于TX和AOP的xml配置文件方式,二种就是基于@Transactional 注解了。

b.编程式事务

是指在代码中手动的管理事务的提交、回滚等操作,代码侵入性比较强。如下所示(手动回滚):

        try {
				//执行代码
            }
            return errorList;
        } catch (Exception e) {
        	  e.getMessage()
            //手动回滚事务
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }

二、 回滚失败的几种情况

最开始我当时直接照葫芦画瓢加了个注释以为就能用了。后来慢慢倒查信息,学到了比较完整的事务管理,所以先记录这个东西。
由于我仅仅需要对单个插入动作进行回滚,并不是整个插入流程(从删除重复到全部插入完毕)。所以最开始我把插入循环提取出来,放在同一个类中并在方法上作了注释。引出了第一个回滚失败的情况。

1. 同一个类中方法调用,导致 @Transactional 失效

这个是我最开始犯的错误。为什么同一个类中的方法被调用 @Transactional就会失效?
这跟spring AOP(埋个点,日后学习下AOP)有关。由于使用spring aop代理,事务的有效范围仅在aop代理能找到的情况。但方法调用同一个类中的内部方法并不会经过aop代理,因此@Transactional失效。
如果整个方法确实需要放在一个类中,这该怎么办呢?只需要在开头声明一下就行了。

public class ImportInfo{
	//  自己声明自己,哈哈哈
	private ImportInfo importInfo;
}

自己声明了自己以后,就会生成对象在AOP中被调用,@Transactional就有用了。

2. 异常被你的 catch“吃了”导致 @Transactional 失效

在意识到第一个错误后,我把循环插入的方法提取出来,扔到了一个工具类中。但仍然不能回滚。这就引出了第二个原因。由于不可抗力的原因,我的循环体中,必须用try catch来捕获异常并返回一个列表。而@Transactional 的触发也是需要有异常报出。这看起来并不冲突,却是“无解”的事情。 @Transactional 是在方法报错后,对整个方法进行回滚。但try catch会把代码块的报错合理化。即,代码报错异常后被catch捕获然后返回对应的措施。在整个方法层面讲,就是没有报错。

那,怎么解决呢?
最开始我和同事打算直接在catch中扔出来一个异常(throw new Exception();)发现IDE直接报错。这是因为这是一个编译时就报错的异常,而不是运行时报错的异常。我的同事,是个狠人,直接int i = 1/0; 整个理论上是可以的,但跟 throw new RuntimeException()一样,属于走bug的方法且无法返回return。
正确的解决方案是,在catch中手动事务回滚

          catch (Exception e) {
            //手动回滚事务
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return  exampleList;
        }

3. @Transactional 注解属性 rollbackFor 设置错误

这个是我在测试@Transactional时注意到的一个方面。当你给@Transactional设定回滚错误类型时,需要注意到这点。你希望发生什么类型的异常时回滚。一般来说,不写就是默认Exception.class。这是最高类型的异常。

4. @Transactional 应用在非 public 修饰的方法上

5. @Transactional 注解属性设置错误

这个涉及事务的传播机制

6. 数据库引擎不支持事务

三、事务的传播机制

①、PROPAGATION_REQUIRED :required , 必须。默认值,A如果有事务,B将使用该事务;如果A没有事务,B将创建一个新的事务。

②、PROPAGATION_SUPPORTS:supports ,支持。A如果有事务,B将使用该事务;如果A没有事务,B将以非事务执行。

③、PROPAGATION_MANDATORY:mandatory ,强制。A如果有事务,B将使用该事务;如果A没有事务,B将抛异常。

④、PROPAGATION_REQUIRES_NEW :requires_new,必须新的。如果A有事务,将A的事务挂起,B创建一个新的事务;如果A没有事务,B创建一个新的事务。

⑤、PROPAGATION_NOT_SUPPORTED :not_supported ,不支持。如果A有事务,将A的事务挂起,B将以非事务执行;如果A没有事务,B将以非事务执行。

⑥、PROPAGATION_NEVER :never,从不。如果A有事务,B将抛异常;如果A没有事务,B将以非事务执行。

⑦、PROPAGATION_NESTED :nested ,嵌套。A和B底层采用保存点机制,形成嵌套事务。
带你读懂Spring 事务——事务的传播机制
这篇文章很详细的讲解了这7种的传播机制以及他们面对回滚时的不同个反应。

四、一些后续(22年12月)

哈哈哈,我也没想到这个能后续记录起来。事情的起因是项目中要用到多数据源配置。在A方法中使用了数据源X,且A方法中调用了B方法,而B方法使用了数据源Y。项目里使用注解@DS来切换数据源。

最开始我也只是使用最简单的@Transactional(rollbackFor = Exception.class)来注解B方法,但自测的时候发现数据源就是切不过去。最后通过查找资料和排查,发现切换数据源和事务产生了冲突。目前,我使用了propagation = Propagation.REQUIRES_NEW这个方法解决了冲突问题,但是否会有事务上的bug出现还未可知,在进一步学习ing。。。。。。
学习资料:SpringBoot多数据源切换详解,以及开启事务后数据源切换失败处理

ps:后来同事也发现了这个问题,在问过我知道了原因以后,他选择把事务删了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值