Spring事务传播机制以及事务嵌套

Spring事务传播机制

Spring 通过切面配置事务传播机制 PROPAGATION

  1. 默认 REQUIRED 支持当前事务,当前没有事务则新建

  2. REQUIRED_NEW 挂起当前事务,新建事务连接

  3. SUPPORTS 支持当前事务,如果没有,则以非事务方式执行(自动提交)

  4. MANDATORY 支持当前事务,如果没有,则抛出异常

  5. NOT_SUPPORTS 以非事务方式执行操作,如果当前事务存在,就把当前事务挂起

  6. NEVER 以非事务方式执行,如果存在事务,则抛出异常

  7. NESTED 实现事务分部分回滚

事务嵌套场景

默认发生的异常都是符合回滚条件的异常

情景0:

methodA开启事务@Transactional(propagation = Propagation.REQUIRED),写库前或者写库后发生异常,methodA都会发生回滚!

场景1:不同类中,开启事务的方法调用没有开启事务的方法

methodA方法嵌套methodB方法,其中methodA开启事务@Transactional(propagation = Propagation.REQUIRED),methodB方法没有添加事务注解:
● 情况1:(默认methodA中无异常发生)methodB方法抛出异常
methodA,methodB 会发生回滚
● 情况2:(默认methodA中无异常发生)methodB方法抛出异常,但是在methodA中将methodB抛出的异常try cache掉了,没有继续向外抛出

1、methodA不会发生回滚,
2、理论上methodB 也不会发生回滚,但是异常发生的位置会影响methodB写库操作是否能成功
    2.1、如果异常是在methodB写库后发生,则写库是成功的,且不会发生回滚
    2.2、如果异常是在methodB写库前发生,此时写库操作还未执行,此时数据未入库并不是因为发生了回滚,而是写库未执行造成的

● 情况3:在methodB执行前,methodA抛出异常

1、methodA发生回滚
2、理论上methodB也会发生回滚,此时methodB未执行

● 情况4:在methodB执行后,methodA抛出异常

1、methodA发生回滚
2、methodB发生回滚

此种情景methodA,methodB属于一个事务连接

场景2:不同类中,methodA方法嵌套methodB方法,且传播机制不同

methodA方法嵌套methodB方法,其中methodA开启事务@Transactional(propagation = Propagation.REQUIRED),methodB方法添加事务注解@Transactional(propagation = Propagation.REQUIRES_NEW):
● 情况1:(默认methodA中无异常发生) methodB方法抛出异常

1、methodA发生回滚,因为methodB发生异常导致
2、methodB发生回滚

● 情况2:(默认methodA中无异常发生) methodB方法抛出异常,但是在methodA中将methodB抛出的异常try cache掉了,没有继续向外抛出

1、methodA写库成功
2、methodB发生回滚

● 情况3:在methodB执行前,methodA抛出异常

1、methodA发生回滚
2、理论上methodB不会发生回滚,此时methodB未执行

● 情况4:在methodB执行后,methodA抛出异常

1、methodA发生回滚
2、理论上methodB不会发生回滚

此种情景methodA,methodB属于不同的事务连接,执行methodB的事务时,会将methodA的事务挂起。

场景3:不同类中,NESTED传播机制的效果

methodA方法嵌套methodB方法,其中methodA开启事务@Transactional(propagation = Propagation.REQUIRED),methodB方法添加事务注解@Transactional(propagation = Propagation.NESTED)
,methodB方法添加事务注解@Transactional(propagation = Propagation.NESTED)


classA中
@Transactional(propagation = Propagation.REQUIRED)
public void methodA(){

    try{
        classB.methodB();
    }catch(Exception e){
        classB.methodC();
    }
    
}

classB中
@Transactional(propagation = Propagation.NESTED)
public void methodB(){
    
}

classB中
@Transactional(propagation = Propagation.NESTED)
public void methodC(){
    
}
methodA在执行methodB前会保存一个savePoint,
当methodB发生异常时,会回滚到savePoint,然后再去执行后续的代码  完善了情景1-情况2中的问题

此种情景methodA,methodB,methodC属于同一个事务连接,该事务具备部分回滚的能力

场景4:不同类中,没有开启事务的方法调用开启事务的方法

methodA方法嵌套methodB方法,其中methodA不开启事务,methodB方法添加事务注解@Transactional(propagation = Propagation.REQUIRED):

  • 情况1:methodB发生异常
1、methodA不回滚
2、methodB会回滚
  • 情况2:methodA发生异常
1、methodA不回滚
2、methodB不回滚

问题:
1、情景1-情况2中,如果methodB也添加@Transactional(propagation = Propagation.REQUIRED),则methodB,methodA都发生回滚

注:情景1-情景4 methodA,methodB在不同的类中

情景5:同一个类中,methodA未开启事务,methodB开启事务

  • 情况1:methodA或methodB发生异常
	public void methodA() {
        OssTask taskCc = new OssTask().setName("testa");
        taskCc.setProjectId("2");
        taskService.save(taskCc);
    
        methodB();
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void methodB() {
        OssTask taskCc = new OssTask().setName("testb");
        taskCc.setProjectId("3");
        taskService.save(taskCc);
        int w = 2/0;  // 抛出异常
    }

结果:
在这里插入图片描述

methodA,methodB 都未发生回滚

  • 情况2:methodB发生异常,但是methodA通过代理类调用methodB
	public void methodA() {
        OssTask taskCc = new OssTask().setName("testa");
        taskCc.setProjectId("2");
        taskService.save(taskCc);
        ModuleServiceImpl moduleService = (ModuleServiceImpl) AopContext.currentProxy();
        moduleService.methodB();

    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void methodB() {
        OssTask taskCc = new OssTask().setName("testb");
        taskCc.setProjectId("3");
        taskService.save(taskCc);
        int w = 2/0;

    }

在这里插入图片描述

此时methodB中事务发生回滚,methodA中无事务所以不发生回滚

情景6:同一个类中,methodA开启事务,methodB未开启事务

  • 情况1:methodB发生异常
	@Transactional(propagation = Propagation.REQUIRED)
    public void methodA() {
        OssTask taskCc = new OssTask().setName("testa");
        taskCc.setProjectId("2");
        taskService.save(taskCc);

        methodB();
    }



//    @Transactional(propagation = Propagation.REQUIRED)
    public void methodB() {
        OssTask taskCc = new OssTask().setName("testb");
        taskCc.setProjectId("3");
        taskService.save(taskCc);
        int w = 2/0;
    }

在这里插入图片描述

methodA,methodB 发生回滚

  • 情况2:methodA发生异常
	@Transactional(propagation = Propagation.REQUIRED)
    public void methodA() {
        OssTask taskCc = new OssTask().setName("testa");
        taskCc.setProjectId("2");
        taskService.save(taskCc);

        methodB();
        int w = 2/0;
    }



//    @Transactional(propagation = Propagation.REQUIRED)
    public void methodB() {
        OssTask taskCc = new OssTask().setName("testb");
        taskCc.setProjectId("3");
        taskService.save(taskCc);
        
    }

同上,发生回滚

综合情景5和情景6:
Spring的声明式事务是基于Aop实现的,将业务逻辑和事务逻辑进行解耦,在Aop的代理下,只有目标方法通过外部调用,目标方法才有代理对象来进行管理,反之如果是内部调用,目标方法还是由原对象管理,此时改方法并没有得到增强处理,也就是会造成自调用问题(事务失效)。
重点在于,Spring采用动态代理(AOP)实现对bean的管理和切片,它为我们的每个class生成一个代理对象。只有在代理对象之间进行调用时,可以触发切面逻辑。
而在同一个class中,方法A调用方法B,调用的是原对象的方法,而不通过代理对象。所以Spring无法切到这次调用,也就无法通过注解保证事务性了。
也就是说,在同一个类中的方法调用,则不会被方法拦截器拦截到,因此事务不会起作用。

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值