Spring事务的一些学习

本文详细解读了7种事务传播级别,包括Propagation_Required、Propagation_Supports等,并提供了实际场景应用示例。重点讲解了事务失败的常见原因及解决策略,帮助开发者避免常见AOP陷阱。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

7种传播级别说明
事务传播行为类型解释说明
Propagation_Required表示被修饰的方法必须运行在事务中。如果当前方法没有事务,则就新建一个事务;如果已经存在一个事务中,就加入到这个事务中。此类型是最常见的默认选择。(如果被调用端发生异常,那么调用端和被调用端事务都将回滚)
Propagation_Supports表示被修饰的方法不需要事务上下文。如果当前方法存在事务,则支持当前事务执行;如果当前没有事务,就以非事务方式执行。
Propagation_Mandatory表示被修饰的方法必须在事务中运行。如果当前事务不存在,则会抛出一个异常。
Propagation_Required_New表示被修饰的方法必须运行在它自己的事务中。一个新的事务会被启动。如果调用者存在当前事务,则在该方法执行期间,当前事务会被挂起。直到新的事务提交或者回滚才恢复执行。
Propagation_Not_Supported表示被修饰的方法不应该运行在事务中。如果调用者存在当前事务,则该方法运行期间,当前事务将被挂起。
Propagation_Never表示被修饰的方法不应该运行事务上下文中。如果调用者或者该方法中存在一个事务正在运行,则会抛出异常。
Propagation_Nested表示当前方法已经存在一个事务,那么该方法将会在嵌套事务中运行。嵌套的事务可以独立与当前事务进行单独地提交或者回滚。如果当前事务不存在,那么其行为与Propagation_Required一样。(嵌套事务的概念就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚。)

示例详见此博客

使用场景
  1. 在一个充值场景中,充值方法中包含扣款和生成订单2个方法,扣款和生成订单都不会被单独调用,因此扣款和生成订单可设置为Propagation_Mandatory,他们必须加入充值方法开启的事务中。
  2. 如果在上述场景中,存在发短信等操作,发短信是允许失败的,且不影响充值本身,那发短信是可以以一个独立的事务开启,但充值的失败是会影响到发短信的,因此可使用Propagation_Nested
  3. 还是上述场景,改成写日志,日志不管充值的成功还是失败都是要写的,且也不影响充值本身,可使用Propagation_REQUIRED_NEW; 如果写日志本身不需要在事务中运行可以使用Propagation_Never
  4. Propagation_Supports常用于逻辑代码的拆分,该方法可以以事务或不以事务的方式加入。
事务失败的场景

在我目前某个OA平台中,有一次大批量数据写入的操作,最多的情况一次性会导入3000多行,该操作由于网络等原因会有一定失败几率,为了方便下次导入不会再导入重复,因此该大操作也是以一个事务的方式运行。
但实际情况则是,操作被中断后,居然是部分成功的,不符合事务中操作是原子性的概念。说明事务没有起作用。
根据网络上一些失效的原因,来分析。

  1. 注解@Transactional配置的方法非public权限修饰;
  2. 注解@Transactional所在类非Spring容器管理的bean;
  3. 注解@Transactional所在类中,注解修饰的方法被类内部方法调用;
  4. 业务代码抛出异常类型非RuntimeException,事务失效;
  5. 业务代码中存在异常时,使用try…catch…语句块捕获,而catch语句块没有throw new RuntimeExecption异常;(最难被排查到问题且容易忽略)
  6. 注解@Transactional中Propagation属性值设置错误即Propagation.NOT_SUPPORTED(一般不会设置此种传播机制)
  7. mysql关系型数据库,且存储引擎是MyISAM而非InnoDB,则事务会不起作用(基本开发中不会遇到);

首先排除第1,2,6,7条,不会犯这种比较傻的错误。但看到第1、2、3条的错误原因,大概推测出这是AOP的一个有坑的点,因为事务是基于AOP这种运行期动态代理,2表示只有加入Spring容器才能由Spring设置动态代理,而1,3条则是表明动态代理有限制,只会代理public方法,而如果被类内部方法使用是不会走代理方法的。

我原来在真没注意到第3、4条的坑,我还真的犯了这个错误。
第3条的改进方式:调用代理类来做,并在启动类上加上@EnableAspectJAutoProxy(exposeProxy = true)

((XxxxxServiceImpl)AopContext.currentProxy()).insertXXXX(xxx);

第4条也是一个被忽视的点,我原先一直是继承Exception并抛出异常的。并不知道只有RuntimeException会影响事务的回滚。或者在@Transactional注解上加上rollbackFor = Exception.class

到此破案了,我这里的主要失败原因是第3条,方法被内部类调用导致没有起作用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值