日常开发中,经常需要用到各种各样的事务,去控制我们的代码的执行,但是如果事务使用的不当,也是会导致事务失效,以下是几个容易导致 Spring 事务失效的场景:
事务失效的场景
具体原因及解决方式
一、手动抛出不支持的异常类型:
- Spring事务默认支持RuntimeException及其子类异常,但手动抛出Exception(RuntimeException的父类)会导致事务失效。
- 解决方案:
-
指定Spring事务异常捕获类型:
@Transactional(rollbackFor = Exception.class)
-
或者抛出Spring事务支持的异常类型:
throw new RuntimeException("手动抛出运行时异常");
-
二、使用了try catch捕获且未抛出异常:
- Spring事务必须在接收到异常的时候才会去触发事务的回滚,因此假设我们在代码中try catch捕获了异常后,处理完自身逻辑后并未抛出异常,就会导致事务失效。
- 解决方案:
- 在catch代码块中抛出异常
三、事务方法无法被spring代理:
- 事务注解 (@Transactional) 仅在公共方法上有效。如果注解应用在 private、protected 或 package-private 方法上,事务将不会生效。spring声明式事务是基于AOP去代理的,只能拦截public修饰的方法
- 解决方案:
- 确保所有带有 @Transactional 注解的方法都是 public 的
四、事务所属类未被spring容器管理:
- spring的事务都是基于AOP去实现的,若事务所在的类未添加@service注解,也就是事务类未注册到spring容器中,那么spring的事务也是无法生效的。
- 解决方案:
- 确保所有带有事务的所属类被@service修饰
五、事务方法的自调用:
- 在代码类中,若一个携带事务注解的方法调用了另一个携带事务注解的方法,那么被调用的方法的事务将会失效。因为spring的事务是基于AOP的动态代理去实现的,而同类方法的自调用或者使用this方法调用,是不会经过代理对象的,所以会导致事务的失效
- 解决方案:
- 方法一:将被调用的方法移到另一个服务类中
- 方法二:将被调用的方法移到另一个服务类中
- 方法三:使用@Autowired在本类中注入本类实例,再用注入的实例对象调用该方法。
- 方法四(推荐):手动获取本类的代理,用代理调用该方法:
((UserService)AopContext.currentProxy()).update();
六、事务所属类未被spring容器管理:
- spring的事务其实都是基于数据库的事务去实现的,部分数据表本身不支持事务,如MySql的MyISAM引擎。
- 解决方案:
- 确保使用的数据库引擎支持事务,如MySql的InnoDB引擎。
七、spring事务传播级别设置不支持事务:
- spring的事务的传播级别若使用的不恰当,也会导致事务的失效,如@Transactional(propagation = Propagation.NOT_SUPPORTED)或@Transactional(propagation = Propagation.NEVER)会导致不支持事务。
- 解决方案:
- 使用Spring默认的传播级别(PROPAGATION_REQUIRED),或其他支持事务的传播级别。
八、代理对象和类加载器调用:
- 当我们使用反射,或者类加载器动态去调用事务方法的时候,会导致事务失效,因为AOP无法拦截这种形式的调用,也就无法获取到代理对象进行事务的管理。
- 解决方案:
- 确保所有带有 @Transactional 注解的方法都通过 Spring 管理的代理对象调用。
九、事务方法被多线程调用:
- 在spring中,事务的上下文都是基于当前线程的,事务管理器无法将上下文中切换到新的线程,新线程中无法继承父线程的事务上下文,因而导致事务失效。
- 解决方案:
- 避免在事务范内启动新线程,或者在多线程环境中使用编程式事务管理。
** 若对您有帮助,希望点赞收藏~**