美文网首页
Transaction rolled back because

Transaction rolled back because

作者: 放开那个BUG | 来源:发表于2024-04-02 23:54 被阅读0次

    一、前言

    如下的代码,两个 service 都有 @Transactional 注解的方法,意味着 methodA() 和 methodB() 都有事务。methodA() 调用 methodB() 方法,methodB() 方法抛异常了,methodA() 里面 try-catch 了。methodA() 原本想的是,B 方法不管成功和失败,都不影响 A 方法的事务。但是实际运行会抛这样的异常:org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only。

    @Service
    public class ServiceA {
      @Autowired
      private ServiceB serviceB;
    
      @Transactional
      public void methodA() {
        try{
          serviceB.methodB();
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
    }
    
    
    @Service
    public class ServiceB {
    
      @Transactional
      public void methodB() {
        throw new RuntimeException();
      }
    }
    

    二、原因

    当使用 @Transactional 注解时,默认设置的事务传播行为是:@Transactional(propagation= Propagation.REQUIRED),它的解释如下。

    • 事务的创建:如果当前没有活动的事务,Propagation.REQUIRED 会创建一个新的事务。这意味着,当一个方法被 @Transactional(Propagation.REQUIRED) 注解时,Spring 会检查当前是否有事务上下文。如果没有,它将启动一个新的事务
    • 事务的嵌套:如果当前已经存在一个事务,Propagation.REQUIRED 将加入到这个已有的事务中。这意味着,如果一个 Propagation.REQUIRED 注解的方法被另一个同样使用 Propagation.REQUIRED 注解的方法调用,它们将会在同一个事务中执行。

    在这种情况下,外层事务(ServiceA)和内层事务(ServiceB)就是一个事务,任何一个出现异常,都会在methodA执行完毕后回滚。
    如果内层事务B抛出异常e(没有catch,继续向外层抛出),在内层事务结束时,spring会把事务B标记为“rollback-only”;这时外层事务A发现了异常e,如果外层事务A catch了异常并处理掉,那么外层事务A的方法会继续执行代码,直到外层事务也结束时,这时外层事务A想commit,因为正常结束没有向外抛异常,但是内外层事务A、B是同一个事务,事务B(同时也是事务A)已经被内层方法标记为“rollback-only”,需要回滚,无法commit,这时spring就会抛出org.springframework.transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only,意思是“事务已经被标记为回滚,无法提交”。

    三、解决方法

    一般这样写的,都是希望内层事务的状态不影响外层事务,外层事务继续执行。
    所以只需要内层事务B是一个新事务,则B的状态不影响A的状态。
    @Transactional(propagation = Propagation.REQUIRES_NEW)

    相关文章

      网友评论

          本文标题:Transaction rolled back because

          本文链接:https://www.haomeiwen.com/subject/otfgtjtx.html