前言

  • 在开发过程中,可能会遇到使用 @Transactional 进行事务管理时出现失效的情况,这里梳理下失效场景

梳理

  • 数据库存储引擎问题
    • MyISAM 不支持事务,改成 InnoDB 引擎则支持事务
  • 方法访问修饰符问题

    • 注解 @Trasactional 只能加在 public 修饰的方法上事务才起效。如果加在 protect、private 等非 public 修饰的方法上,事务将失效。
    • 因为Spring声明式事务是通过动态代理来实现的,private方法不能被继承,final方法不能被重写,static方法和继承不相干,所以它们3个的事务不起作用
    • public方法,protected方法可以被重写以添加事务代码
  • 方法异常处理问题

    • 如果在开启了事务的方法内,使用了 try-catch 语句块对异常进行了捕获,而没有将异常抛到外层,事务将不起效。
    • @Trasactional 只对运行时异常处理,不会对编译型异常回滚,所以一般在项目中需要加上这个@Transactional(rollbackFor = Exception.class)
  • 一个类中的方法调用

    • 是否发生了自调用,只有当事务方法被当前类以外的代码调用时,才会由 spring 生成代理对象管理事务;(在当前类中调用事务方法,因为没有经过spring的代理类,默认只有在外部调用才会生效)
    • 同一个类中,methodA 没有设置事务,methodB 设置了事务,methodA 调用 methodB 时,事务失效;
    • 事务无效示例1:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      @Service
      public class OrderServiceImpl implements OrderService {

      public void update(Order order) {
      updateOrder(order);
      }

      @Transactional
      public void updateOrder(Order order) {
      // update order
      }

      }

总结

  • Spring 团队建议在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。在接口上使用 @Transactional 注解,只能当你设置了基于接口的代理时它才生效。因为注解是不能继承的,这就意味着如果正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装。

参考