Spring 事务的传播机制

200 阅读10分钟

一、事务

事务的传播机制

事务的传播机制是指 在两个或多个事务内,事务相互之间的影响或传递

事务传播机制的类别

  • REQUIRED: 支持当前事务,如果当前没有事务,就新建一个事务,这是最常见的选择
  • SUPPORTS: 支持当前事务,如果当前没有事务,就以非事务方式执行
  • MANDATORY: 支持当前事务,如果当前没有事务,就抛出异常
  • REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起
  • NOT_SUPPORTED:以非事务方式操作,如果当前存在事务,就把当前事务挂起。
  • NEVER:以非事务方式执行,如果当前存在事务,则抛出异常
  • NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。
REQUIRED

这个是事务的默认取值,当 ServiceA.methodA 的事务级别是 REQUIRED, ServiceB.methodB 也是 REQUIRED 时,ServiceA.methodA 起了一个事务

ServiceB.methodBServiceB.methodB 调用,ServiceB.methodB 发现自己在事务中,就不在新起事务了,如果 ServiceA.methodA 没有起事务

ServiceB.methodB 自己起事务。

当前存在事务中则加入当前事务,如果不在事务中则起一个事务

代码演示 场景1:在上层方法加入事务注解,下方不加

	@Autowired
    private TransactionalService transactionalService;

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void testTrans(){
        transactionalService.saveParent();
        transactionalService.saveChild();
    }

ServiceA
--------------------------------------------------------------------------------------------------------------------------------
ServiceB  

	@Override
    public void saveParent() {
        this.save(new SysRoleDept().setRoleId("1").setDeptId("parent"));
    }

    @Override
    public void saveChild() {
        saveChild1();

        // 模拟异常
        int i = 1 / 0;

        saveChild2();
    }
    private void saveChild1(){
        this.save(new SysRoleDept().setRoleId("2").setDeptId("child1"));
    }


    private void saveChild2(){
        this.save(new SysRoleDept().setRoleId("3").setDeptId("child2"));
    }

执行结果:

image-20230513000350654.png

发生异常,失败回滚,结论: 当前存在事务中则加入当前事务

代码演示 场景2:上层事务关闭,开启下层事务

	@Autowired
    private TransactionalService transactionalService;


    // @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void testTrans(){
        transactionalService.saveParent();
        transactionalService.saveChild();
    }


ServiceA
--------------------------------------------------------------------------------------------------------------------------------
ServiceB  
    
    
	@Override
    public void saveParent() {
        this.save(new SysRoleDept().setRoleId("1").setDeptId("parent"));
    }

    @Override
	@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void saveChild() {
        saveChild1();

        // 模拟异常
        int i = 1 / 0;

        saveChild2();
    }
    private void saveChild1(){
        this.save(new SysRoleDept().setRoleId("2").setDeptId("child1"));
    }


    private void saveChild2(){
        this.save(new SysRoleDept().setRoleId("3").setDeptId("child2"));
    }

执行结果:

image-20230513000902606.png 发生异常,失败回滚,但是 上层方法保存成功,结论: 如果不在事务中则起一个事务

SUPPORTS

当标注的方法在事务中,则按事务的方式执行,此时类似标注 REQUIRED, 外层回滚一起回滚,其本身不会发生事务,当外层没有事务时,则按没有事务的方式执行

如果调用此方法的方法存在事务,则本方法存在事务,与调用者一体事务,如果调用者没有事务,此方法以非事务方式运行

代码演示 场景1:上层事务开启 REQUIRED 下层事务开启 SUPPORTS

	@Autowired
    private TransactionalService transactionalService;


    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void testTrans(){
        transactionalService.saveParent();
        transactionalService.saveChild();
    }

ServiceA
--------------------------------------------------------------------------------------------------------------------------------
ServiceB  

	@Override
    public void saveParent() {
        this.save(new SysRoleDept().setRoleId("1").setDeptId("parent"));
    }

    @Override
	@Transactional(propagation = Propagation.SUPPORTS,rollbackFor = Exception.class)
    public void saveChild() {
        saveChild1();

        // 模拟异常
        int i = 1 / 0;

        saveChild2();
    }
    private void saveChild1(){
        this.save(new SysRoleDept().setRoleId("2").setDeptId("child1"));
    }


    private void saveChild2(){
        this.save(new SysRoleDept().setRoleId("3").setDeptId("child2"));
    }

执行结果:

image-20230513163449497.png

发生异常,失败回滚,未插入数据,结论:如果调用此方法的方法存在事务,则本方法存在事务,与调用者一体事务

代码演示,场景2 : 上层事务关闭,下层事务开启 SUPPORTS

	@Autowired
    private TransactionalService transactionalService;


    // @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void testTrans(){
        transactionalService.saveParent();
        transactionalService.saveChild();
    }

ServiceA
--------------------------------------------------------------------------------------------------------------------------------
ServiceB  
    
    
	@Override
    public void saveParent() {
        this.save(new SysRoleDept().setRoleId("1").setDeptId("parent"));
    }

    @Override
	@Transactional(propagation = Propagation.SUPPORTS,rollbackFor = Exception.class)
    public void saveChild() {
        saveChild1();

        // 模拟异常
        int i = 1 / 0;

        saveChild2();
    }
    private void saveChild1(){
        this.save(new SysRoleDept().setRoleId("2").setDeptId("child1"));
    }


    private void saveChild2(){
        this.save(new SysRoleDept().setRoleId("3").setDeptId("child2"));
    }

执行结果

image-20230513163744834.png

执行结果,上层事务保存成功,发生异常后 下层事务没有回滚,结论 :如果调用者没有事务,此方法以非事务方式运行

MANDATORY

调用者必须存在事务,如果调用者没有存在事务,则抛出异常

代码演示, 场景1:上层事务关闭,下层开启 MANDATORY 事务

	@Autowired
    private TransactionalService transactionalService;


    // @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void testTrans(){
        transactionalService.saveParent();
        transactionalService.saveChild();
    }


ServiceA
--------------------------------------------------------------------------------------------------------------------------------
ServiceB  
    
    
	@Override
    public void saveParent() {
        this.save(new SysRoleDept().setRoleId("1").setDeptId("parent"));
    }

    @Override
	@Transactional(propagation = Propagation.MANDATORY,rollbackFor = Exception.class)
    public void saveChild() {
        saveChild1();

        // 模拟异常
        int i = 1 / 0;

        saveChild2();
    }
    private void saveChild1(){
        this.save(new SysRoleDept().setRoleId("2").setDeptId("child1"));
    }


    private void saveChild2(){
        this.save(new SysRoleDept().setRoleId("3").setDeptId("child2"));
    }

执行结果:

image-20230513164139005.png

上层事务保存成功,下层因为没有包含事务,抛出异常,中止执行

image-20230513164216724.png

结论:调用者必须存在事务,如果调用者没有存在事务,则抛出异常

REQUIRES_NEW

ServiceA.methodA 的事务级别是 REQUIRED , ServiceB.methodBREQUIRES_NEW 时, ServiceA.methodA 起了一个事物, ServiceB.methodB

ServiceA.methodA 调用,则挂起 ServiceA.methodA 的事务,自己重新起事务,这两个事务是隔离的,当 ServiceB.methodB 提交之后,才继续 ServiceA.methodA 的事务,他与 REQUIRED 的区别在于回滚程度, 因为 ServiceB.methodB 是新起一个事务,那么就是存在两个不同的事务,ServiceA.methodA 的事务失败回滚,也不会影响到 ServiceB.methodB 的事务。 如果 ServiceB.methodB 发生了异常回滚,ServiceA.methodA 可以选择是否捕捉异常继续提交,或者 与其一起回滚。

重新起事务,不管调用者是否存在事务,如果存在则挂起调用者事务,等待本身事务执行完毕,再执行调用者事务。事务相互隔离,回滚失败与否 互不影响,但是抛出的异常外层事务是可以捕获的

代码演示,场景1:上层事务开启 REQUIRED , 下层事务开启 REQUIRES_NEW, 异常模拟在 下层事务中

	@Autowired
    private TransactionalService transactionalService;


    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void testTrans(){
        transactionalService.saveParent();
        transactionalService.saveChild();
    }

ServiceA
--------------------------------------------------------------------------------------------------------------------------------
ServiceB  

	@Override
    public void saveParent() {
        this.save(new SysRoleDept().setRoleId("1").setDeptId("parent"));
    }

    @Override
	@Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
    public void saveChild() {
        saveChild1();

        // 模拟异常
        int i = 1 / 0;

        saveChild2();
    }
    private void saveChild1(){
        this.save(new SysRoleDept().setRoleId("2").setDeptId("child1"));
    }


    private void saveChild2(){
        this.save(new SysRoleDept().setRoleId("3").setDeptId("child2"));
    }

执行结果:

image-20230513163449497.png

失败回滚,上层方法也没有插入,这样看, REQUIRES_NEW 和上层事务好像是一体的,看一下场景2

代码演示 场景2: 上层事务开启 REQUIRED 下层事务开启 REQUIRES_NEW 模拟异常发生在,上层事务

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void testTrans(){
        transactionalService.saveParent();
        transactionalService.saveChild();

        // 模拟异常
        int i = 1 / 0;
    }


ServiceA
--------------------------------------------------------------------------------------------------------------------------------
ServiceB      
    
    @Override
    public void saveParent() {
        this.save(new SysRoleDept().setRoleId("1").setDeptId("parent"));
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
    public void saveChild() {
        saveChild1();

        // 模拟异常
//        int i = 1 / 0;

        saveChild2();
    }
    private void saveChild1(){
        this.save(new SysRoleDept().setRoleId("2").setDeptId("child1"));
    }


    private void saveChild2(){
        this.save(new SysRoleDept().setRoleId("3").setDeptId("child2"));
    }

执行结果:

image-20230513171638255.png

下层事务保存成功,上层事务发生回滚,结论:标注 REQUIRES_NEW 的事务,是单独的,与上层事务无关。

代码演示,场景3: 上层事务开启 REQUIRED 下层事务开启 REQUIRES_NEW 模拟异常发生在下层事务,上层事务捕捉异常

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void testTrans(){
        transactionalService.saveParent();
        try {
            transactionalService.saveChild();
        } catch (Exception e) {
            log.info("出错了");
        }
    }


ServiceA
--------------------------------------------------------------------------------------------------------------------------------
ServiceB    
    
    @Override
    public void saveParent() {
        this.save(new SysRoleDept().setRoleId("1").setDeptId("parent"));
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
    public void saveChild() {
        saveChild1();

        // 模拟异常
        int i = 1 / 0;

        saveChild2();
    }
    private void saveChild1(){
        this.save(new SysRoleDept().setRoleId("2").setDeptId("child1"));
    }


    private void saveChild2(){
        this.save(new SysRoleDept().setRoleId("3").setDeptId("child2"));
    }

执行结果:

image-20230515223445628.png

外层事务可捕捉内层事务异常

​ 捕捉:内层事务回滚,外层事务照常提交

​ 不捕捉:内外层事务一起回滚。

NOT_SUPPORTED

当标注的方法在事务中,则按照没有事务的方式执行,此时会挂起调用者事务,等待本身执行完毕后,再执行调用者事务,与 REQUIRES_NEW 类似,事务相互隔离,但是标注 NOT_SUPPORTED 是以没有事务的方式执行的,操作失败也不会回滚,但是异常能被外层捕获。

隔离外层事务,且本身不起事务,当外层有事务时挂起外层事务,等待本身执行完毕,再执行外层事务。外层回滚不影响本身操作,但抛出的异常外层是可以捕获到的

代码演示 场景1:上层事务关闭,下层事务为 NOT_SUPPORTED 模拟异常发生在 下层事务

    // @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void testTrans(){
        transactionalService.saveParent();
        transactionalService.saveChild();
    }


ServiceA
--------------------------------------------------------------------------------------------------------------------------------
ServiceB  
    
    
    @Override
    public void saveParent() {
        this.save(new SysRoleDept().setRoleId("1").setDeptId("parent"));
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED,rollbackFor = Exception.class)
    public void saveChild() {
        saveChild1();

        // 模拟异常
        int i = 1 / 0;

        saveChild2();
    }
    private void saveChild1(){
        this.save(new SysRoleDept().setRoleId("2").setDeptId("child1"));
    }


    private void saveChild2(){
        this.save(new SysRoleDept().setRoleId("3").setDeptId("child2"));
    }

执行结果:

image-20230513180437775.png

上层保存成功,下层异常之后未保存,结论:NOT_SUPPORTED 是以没有事务的方式执行的,操作失败也不会回滚,但是异常能被外层捕获。

代码演示 场景2:上层事务开启 REQUIRED,下层事务开启 NOT_SUPPORTED 模拟异常发生在 下层

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void testTrans(){
        transactionalService.saveParent();
        transactionalService.saveChild();
    }


ServiceA
--------------------------------------------------------------------------------------------------------------------------------
ServiceB  
    
    
    @Override
    public void saveParent() {
        this.save(new SysRoleDept().setRoleId("1").setDeptId("parent"));
    }

    @Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED,rollbackFor = Exception.class)
    public void saveChild() {
        saveChild1();

        // 模拟异常
        int i = 1 / 0;

        saveChild2();
    }
    private void saveChild1(){
        this.save(new SysRoleDept().setRoleId("2").setDeptId("child1"));
    }


    private void saveChild2(){
        this.save(new SysRoleDept().setRoleId("3").setDeptId("child2"));
    }

执行结果:

image-20230513180801640.png

上层事务被回滚,下层异常之后不执行,结论:NOT_SUPPORTED 是以没有事务的方式执行的,操作失败也不会回滚,但是异常能被外层捕获。

NEVER

ServiceA.methodA 的事务级别是 REQUIRED, ServiceB.methodBNEVER 时, ServiceA.methodA 起了一个事务, ServiceB.methodBServiceA.methodA 调用时,ServiceB.methodB 就会抛出异常

不能在事务中运行,否则,自身抛出异常

代码演示,场景1:上层事务开启 REQUIRED 下层事务开启 NEVER 模拟异常发生在下层

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void testTrans(){
        transactionalService.saveParent();
        transactionalService.saveChild();
    }


ServiceA
--------------------------------------------------------------------------------------------------------------------------------
ServiceB  
    
    
    @Override
    public void saveParent() {
        this.save(new SysRoleDept().setRoleId("1").setDeptId("parent"));
    }

    @Override
    @Transactional(propagation = Propagation.NEVER,rollbackFor = Exception.class)
    public void saveChild() {
        saveChild1();

        // 模拟异常
        int i = 1 / 0;

        saveChild2();
    }
    private void saveChild1(){
        this.save(new SysRoleDept().setRoleId("2").setDeptId("child1"));
    }


    private void saveChild2(){
        this.save(new SysRoleDept().setRoleId("3").setDeptId("child2"));
    }

执行结果:无保存记录,下层事务抛出异常

image-20230513181154623.png

结论:NEVER 不能在事务中运行,否则,自身抛出异常

NESTED

这个级别和 REQUIRES_NEW 理解基本一样,不同在于 NESTED 不是重新起一个事务,而是建立一个 savePoint , 提交时间和外层事务一起提交,一起回滚

但是好处在于 他存在一个 savePoint 例如自身 失败回滚,外层可以选择新分支执行并尝试完成自己的事务,外层没有事务时,独立运行事务

开始一个嵌套事务,他是属于调用者的子事务,嵌套事务开始时,他会取得一个 savePoint 如果这个嵌套事务失败,我们将回滚到此 savePoint, 外层可以继续执行。嵌套事务是外部事务的一部分,只有外部事务结束后他才会被提交。外部事务失败回滚,内部也会回滚

代码演示, 场景1 : 上层开启事务 REQUIRED 下层开启事务 NESTED 模拟异常发生在 下层事务

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void testTrans(){
        transactionalService.saveParent();
        transactionalService.saveChild();
    }


ServiceA
--------------------------------------------------------------------------------------------------------------------------------
ServiceB    
    
    @Override
    public void saveParent() {
        this.save(new SysRoleDept().setRoleId("1").setDeptId("parent"));
    }

    @Override
    @Transactional(propagation = Propagation.NEVER,rollbackFor = Exception.class)
    public void saveChild() {
        saveChild1();

        // 模拟异常
        int i = 1 / 0;

        saveChild2();
    }
    private void saveChild1(){
        this.save(new SysRoleDept().setRoleId("2").setDeptId("child1"));
    }


    private void saveChild2(){
        this.save(new SysRoleDept().setRoleId("3").setDeptId("child2"));
    }

结果: image-20230513163449497.png

未插入任何数据,因为 上层没有捕捉下层事务 的异常情况

代码演示,场景2:上层开启事务 REQUIRED 下层开启事务 NESTED 模拟异常发生在 下层事务,上层捕捉异常

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void testTrans(){
        transactionalService.saveParent();
        try {
            transactionalService.saveChild();
        } catch (Exception e) {
            log.info("出错了");
        }
    }


ServiceA
--------------------------------------------------------------------------------------------------------------------------------
ServiceB    
    
    @Override
    public void saveParent() {
        this.save(new SysRoleDept().setRoleId("1").setDeptId("parent"));
    }

    @Override
    @Transactional(propagation = Propagation.NEVER,rollbackFor = Exception.class)
    public void saveChild() {
        saveChild1();

        // 模拟异常
        int i = 1 / 0;

        saveChild2();
    }
    private void saveChild1(){
        this.save(new SysRoleDept().setRoleId("2").setDeptId("child1"));
    }


    private void saveChild2(){
        this.save(new SysRoleDept().setRoleId("3").setDeptId("child2"));
    }

执行结果:

image-20230515223445628.png

saveParent 保存成功,saveChild 发送异常回滚,结论 NESTED 自身 失败回滚,外层可以选择新分支执行并尝试完成自己的事务

仅仅是这样是不是觉得 NESTEDREQUIRES_NEW 一样,外层可以决定 内部异常 外部是否回滚。

来,往下看,上层发生异常下层也要回滚

代码演示,场景3:上层开启事务 REQUIRED 下层开启事务 NESTED 模拟异常发生在 上层事务

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void testTrans(){
        transactionalService.saveParent();
      
        transactionalService.saveChild();
        
        // 模拟异常
        int i = 1 / 0;
    }


ServiceA
--------------------------------------------------------------------------------------------------------------------------------
ServiceB    
    
    @Override
    public void saveParent() {
        this.save(new SysRoleDept().setRoleId("1").setDeptId("parent"));
    }

    @Override
    @Transactional(propagation = Propagation.NEVER,rollbackFor = Exception.class)
    public void saveChild() {
        saveChild1();

        saveChild2();
    }
    private void saveChild1(){
        this.save(new SysRoleDept().setRoleId("2").setDeptId("child1"));
    }


    private void saveChild2(){
        this.save(new SysRoleDept().setRoleId("3").setDeptId("child2"));
    }

执行结果:

image-20230513163449497.png 上层执行失败,下层也要回滚,与 REQUIRES_NEW 不同,REQUIRES_NEW 是另外再开启一个事务,而 NESTED 是嵌套在上层事务中的子事务,当外层没有事务时,独立运行事务。

事务失效的一些场景
  • @Transactional 加在 非 public 方法上 , 事务通过 aop 切面 进行拦截,非 public 方法,会直接返回 null
  • 在本对象内调用,如 this.method ,通过 this 调用,没有代理对象,除非,在本类中,注入自己本身
  • 事务方法被 finalstatic 修饰 , 这样修饰,代理类无法重写
  • 数据库不支持事务
  • 未开启事务
  • 事务对象未被 Spring 管理
  • 进行 try catch 捕捉, 仅是不回滚事务,可以在 @Transactional 指定要回滚的异常类型
注意事项:

在测试的过程中遇到的一些问题

1.一定要使用两个 Service 来测试,不要在 单元测试上直接加 事务,因为 Spring 的事务 是根据 代理 进行拦截的,不通过注入无法创建代理对象