spring事务的传播行为

1,290 阅读5分钟

一、事务的传播行为分类

根据 spring 源码中 org.springframework.transaction.annotation.Propagation 枚举类中的定义,总共有 7 中传播行为:

传播行为分类说明
REQUIRED(默认的传播行为)1、使用当前事务,如果当前没有事务,会新建一个事务
2、子方法也会运行在父方法的事务中,如果子方法存在事务,则会加入父方法的事务中
SUPPORTS当前存在事务,就使用事务;当前不存在事务,就不使用
MANDATORY当方法的事务传播行为设置为 MANDATORY 时,其它任何方法调用该方法时,都必须存在事务,否则抛出异常
REQUIRES_NEW1、如果当前有事务,挂起当前事务,新建一个事务给自己使用
2、如果当前没有事务,则新建一个事务
NOT_SUPPORTED不管当前有无事务,都不使用事务
NEVER不使用事务,如果调用方当前存在事务,抛出异常
NESTED开启一个子事务(属于嵌套事务),嵌套事务是独立提交或者回滚的

二、简要示例

下面通过一些伪代码,来描述几种常用或者容易混淆的传播行为:

REQUIRED

1、父方法中不存在事务,子方法中也不存在事务

class parentClass {
  // 父方法没有事务
  void parentMethod(){
      // 父方法中调用子方法
      subClass.subMethod();
      ...
  }
}

class subClass {
  // 子方法中也没有事务
  void subMethod(){
      ...
  }
}

这种情况下,出现异常之后,子父方法中的数据都不会回滚

2、父方法中存在事务,子方法中不存在事务

class parentClass {
  // 父方法存在事务
  @Transactional(propagation = Propagation.REQUIRED)
  void parentMethod(){
      // 父方法中调用子方法
      subClass.subMethod();
      ...
  }
}

class subClass {
  // 子方法中没有事务
  void subMethod(){
      ...
  }
}

这种情况下,父方法的事务会传递到子方法中,所以子父方法中只要出现异常,都会回滚数据

3、父方法中不存在事务,子方法中存在事务

class parentClass {
  // 父方法中不存在事务
  void parentMethod(){
      // 父方法中调用子方法
      subClass.subMethod();
      ...
  }
}

class subClass {
  // 子方法中存在事务
  @Transactional(propagation = Propagation.REQUIRED)
  void subMethod(){
      ...
  }
}
  • 这种情况下,如果子方法中出现异常,子方法中的数据会回滚,但是父方法中的数据不会回滚
  • 如果是子方法执行完成后,父方法出现异常,这时子方法的数据不会回滚,因为事务已经提交。父方法的数据也不会回滚,因为父方法没有事务

4、父方法中存在事务,子方法中也存在事务

class parentClass {
  // 父方法中存在事务
  @Transactional(propagation = Propagation.REQUIRED)
  void parentMethod(){
      // 父方法中调用子方法
      subClass.subMethod();
      ...
  }
}

class subClass {
  // 子方法中也存在事务
  @Transactional(propagation = Propagation.REQUIRED)
  void subMethod(){
      ...
  }
}

这种情况下,子方法会加入到父方法的事务中,所以子父方法中只要出现异常,都会回滚数据


MANDATORY

1、父方法不存在事务

class parentClass {
  // 父方法中不存在事务
  void parentMethod(){
      // 父方法中调用子方法
      subClass.subMethod();
      ...
  }
}

class subClass {
  // 子方法中存在事务
  @Transactional(propagation = Propagation.MANDATORY)
  void subMethod(){
      ...
  }
}

这种情况下,父方法在调用子方法时,会直接抛出异常,因为父方法没有事务

2、父方法存在事务

class parentClass {
  // 父方法中存在事务
  @Transactional(propagation = Propagation.REQUIRED)
  void parentMethod(){
      // 父方法中调用子方法
      subClass.subMethod();
      ...
  }
}

class subClass {
  // 子方法中也存在事务
  @Transactional(propagation = Propagation.MANDATORY)
  void subMethod(){
      ...
  }
}

这种情况下,父方法调用子方法时,是存在事务的,所以不会抛出异常。此时父方法的事务会传递到子方法中,所以只要是子父方法中出现异常,都会回滚数据


REQUIRES_NEW

1、父方法中不存在事务

class parentClass {
  // 父方法中不存在事务
  void parentMethod(){
      // 父方法中调用子方法
      subClass.subMethod();
      ...
  }
}

class subClass {
  // 子方法中存在事务
  @Transactional(propagation = Propagation.REQUIRES_NEW)
  void subMethod(){
      ...
  }
}

这种情况下,如果子方法出现异常,子方法的数据会回滚。父方法中的数据不会回滚

2、父方法中存在事务

class parentClass {
  // 父方法中存在事务
  @Transactional(propagation = Propagation.REQUIRES)
  void parentMethod(){
      // 父方法中调用子方法
      subClass.subMethod();
      ...
  }
}

class subClass {
  // 子方法中存在事务
  @Transactional(propagation = Propagation.REQUIRES_NEW)
  void subMethod(){
      ...
  }
}

这种情况下,需要注意:

  • 如果子方法中出现了异常,子方法的数据会回滚,子方法的异常会传递到父方法中,父方法的数据也会回滚掉
  • 如果子方法中没有出现异常,但是父方法在执行完子方法后,出现了异常,这时只会回滚父方法的数据,子方法的数据不会回滚,因为子方法的事务已经提交

NESTED

重点说下 NESTED 和 REQUIRED 的区别: image.png image.png 伪代码演示如下:

1、子父方法都是 Propagation.REQUIRED

class ParentClass {
    @Transactional(propagation = Propagation.REQUIRED)
    public void parentMethod() {
      // 父方法插入数据  
      stuMapper.insertSelective("父方法插入数据");
      // 调用子方法
      subClass.subMethod();
    }
}

class SubClass {
    @Transactional(propagation = Propagation.REQUIRED)
    public void subMethod() {
        // 插入数据
        stuMapper.insertSelective("子方法插入数据");
        int i = 1 / 0;
    }
}

这种情况下,subMethod() 会加入到 parentMethod() 的事务中去,如果 subMethod() 出现异常,则会直接回滚掉整个事务

2、子方法是 Propagation.NESTED

class ParentClass {
    @Transactional(propagation = Propagation.REQUIRED)
    public void parentMethod() {
      // 父方法插入数据  
      stuMapper.insertSelective("父方法插入数据");
      // 调用子方法
      try {
          // 调用subMethod()方法时,开启了一个嵌套事务,如果subMethod()出现异常,不会回滚parentMethod()的事务,嵌套事务独立回滚
          subClass.subMethod();
      } catch (Exception e) {
          // 打印日志
      }
      // 继续执行其它方法
    }
}

class SubClass {
    @Transactional(propagation = Propagation.NESTED)
    public void subMethod() {
        // 插入数据
        stuMapper.insertSelective("子方法插入数据");
        int i = 1 / 0;
    }
}

这种情况下,subMethod()开启了一个嵌套事务,如果subMethod()出现异常,不会回滚parentMethod()的事务,因为嵌套事务会独立回滚。