spring 事物传播级别

理解Spring事务的七大传播级别
本文详细介绍了Spring中七个不同的事务传播级别,包括PROPAGATION_REQUIRED、PROPAGATION_SUPPORTS、PROPAGATION_MANDATORY、PROPAGATION_REQUIRES_NEW、PROPAGATION_NOT_SUPPORTED、PROPAGATION_NEVER和PROPAGATION_NESTED。这些级别决定了在嵌套事务中多个事务之间的行为,如是否创建新事务、是否加入现有事务、是否回滚等。文章通过实例解析了每个级别的具体应用场景和事务处理逻辑。

Spring事物传播级别:

在处理Spring托管事务时,开发人员能够指定事务在传播方面的行为。换句话说,开发人员能够决定如何将业务方法封装在逻辑事务或物理事务中。不同Spring bean的方法可以在相同的事务范围内执行,也可以跨多个嵌套事务执行。由于每个方法可能都有自己的事物,那么这些方法的事物相互会影响,比如内部事务结果如何影响外部事务,又或外部的事物是如何影响内部事物。Spring 提供了7 中事物传播级别来决定事务的控制范围。
换一句话说,在实际开发中,方法之间各种嵌套调用,这个时候如果嵌套方法各自都有事物,比如A 调用 B,A,B 同时存在事物,这个时候上下文中 存在两个事物,那么这两个事物是如何相互影响; srping 通过事物传播级别 来决定在嵌套事物中,多个事物之间的行为;比如在A 方法已经存在事物了,那么当B 方法执行的时候,B 方法运行在A的事物下,又或者 A 事物挂起,B 方法开启新的事物等等,这些都是有事物传播级别来决定的;

下面将通过 A 方法调用B 方法 例子来说明 不同的事物传播级别:

1、PROPAGATION_REQUIRED

spring 默认的事物传播级别;如果被声明了该事物级别的B 方法在执行的时候发现A 方法中 已经存在事物,那么B 方法将不开启新的事物,加入到A 的事物中,也就是说,接下来A B 方法的事物行为是一致,任意一方抛出异常都将回滚; 如果A不存在事物,B 将新建事物;

OuterService:
@Override
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void testRequired(){
        User user = new User(null, "demo1", 23, 1);
        //结果:内部异常导致外部事物也回滚
        userMapper.insert(user);
        try {
            //抛出异常
            innerService.testRequired();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

由于 内部方法抛出一个异常,不会立即被当前的try 捕获,异常会被事物中try -catch 捕获会当将当前事物标记为 rollBackOnly;然后才会进入到外部的try-catch; 由于外部方法有捕获异常,方法将正常提交事物,在提交事物的时候发现当前事物已经被标记为rollBackOnly,所以整个事物回滚;

InnerService:
 @Override
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void testRequired(){
        throw new RuntimeException();
    }

2、PROPAGATION_SUPPORTS

如果被声明了该事物级别的B 方法在执行的时候发现 A 方法中 已经存在事物,那么B 方法将不开启新的事物,加入到A 的事物中(同REQUIRED一样),也就是说,接下来A B 方法的事物行为是一致,任意一方抛出异常都将回滚;如果A不存在事物,B 将以非事物的方式方式执行;


3、PROPAGATION_MANDATORY

MANDATORY 只是声明当前方法必须在事务中执行。如果没有,容器将抛出异常。

OuterService:
 @Override
 //由于内部方法声明为MANDATORY,当把外部事物注释,在执行的时候就会报错,内部方式声明必须在事物上下文中执行
    //@Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void testMandatory(){
        User user = new User(null, "outer_nested", 23, 1);
        userMapper.insert(user);
        try {
            innerService.testNested();
        } catch (Exception e) {
            e.printStackTrace();
        }
    } 
@Override
  //强制该方法必须在事物环境下执行
    @Transactional(propagation = Propagation.MANDATORY,rollbackFor = Exception.class)
    public void testMandatory(){
        //TODO
    }

**注意: 在执行 innerService.testNested() 前,在获取当前事物的时候,会判断当前上下文是否存在事物,不存在将抛出异常:No existing transaction found for transaction marked with propagation 'mandatory'


4、PROPAGATION_REQUIRES_NEW

REQUIRES_NEW:如果被声明了该事物级别的B 方法在执行的时候发现 A 方法中 已经存在事物,那么会先挂起A 的事物,然后B 新开启事物 ,当B事物回滚或者提交后将重新激活A的事物。换句话说,B事务可以独立于A事务提交或回滚,即外部事务不会受到内部事务结果的影响:它们将在不同的物理事务中运行。

OuterService:
@Override
    @Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
    public void testRequiredNew(){
        User user = new User(null, "REQUIRES_NEW", 23, 1);
        //内部方法抛出异常 ,独立的内部事物回滚,但外部事物不受内部方法事物影响,正常提交
        userMapper.insert(user);
        try {
            innerService.testRequiredNew();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
InnerService:
@Override
    @Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
    public void testRequiredNew(){
        throw new RuntimeException();
    }

5、PROPAGATION_NOT_SUPPORTED

NOT_SUPPORTED:声明方法在非事物状态执行。如果调用者事务已经存在,则挂起调用者事物。

OuterService:
@Override
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void testNotSupports() {
        User user = new User(null, "outer_not_supported", 23, 1);
        userMapper.insert(user);
        try {
            innerService.testNotSupported();
        } catch (Exception e) {
            e.printStackTrace();
        }
        throw new RuntimeException();
    }
InnerService:
@Override
    @Transactional(propagation = Propagation.NOT_SUPPORTED,rollbackFor = Exception.class)
    public void testNotSupported(){
        User user = new User(null, "inner_not_supported", 23, 1);
        //正常插入,因为当前方法是在非事物环境下执行
        userMapper.insert(user);
        //内部方法抛异常不会影响外部事物
        //throw new RuntimeException();
    }

6、PROPAGATION_NEVER

该事物级别和 PROPAGATION_MANDATORY 刚好相反;方法以非事务的状态执行,如果上下文存在事务,则抛出异常;
注意:在执行 innerService.testNested() 前,在获取当前事物的时候,会判断当前上下文是否存在事物,存在将抛出异常:Existing transaction found for transaction marked with propagation 'never''


7、PROPAGATION_NESTED

嵌套级别事务。该传播级别特征是,如果上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务;因为保存点是用JDBC保存点实现的,所以这种行为应该只在Spring JDBC托管事务中使用。这种传播级别跟其他不同的是:内外事物属于父子关系。子事物属于父事物的一部分。

那么什么是嵌套事务呢?引用其他博客
嵌套是子事务套在父事务中执行,子事务是父事务的一部分,在进入子事务之前,父事务建立一个回滚点,叫save point,然后执行子事务, 即使子事物抛出异常,不会真正回滚,只会重置save point ,之后父事务继续执行。重点就在于那个save point。看几个问题就明了了:
如果子事务回滚,会发生什么?
父事务会回滚到进入子事务前建立的save point,然后尝试其他的事务或者其他的业务逻辑,父事务之前的操作不会受到影响,更不会自动回滚。
父事务回滚,子事务也会跟着回滚!为什么呢,因为父事务结束之前,子事务是不会提交的,我们说子事务是父事务的一部分,正是这个道理。那么:
事务的提交,是什么情况?
是父事务先提交,然后子事务提交,还是子事务先提交,父事务再提交?答案是第二种情况,还是那句话,子事务是父事务的一部分,由父事务统一提交。

OuterService:
@Override
    @Transactional(propagation = Propagation.NESTED,rollbackFor = Exception.class)
    public void testNested(){
        User user = new User(null, "outer_nested", 23, 1);
        //如果子事务回滚, 父事务会回滚到进入子事务前建立的save point,然后尝试其他的事务或者其他的业务逻辑,父事务之前的操作不会受到影响,更不会自动回滚。
        userMapper.insert(user);
        try {
            innerService.testNested();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
InnerService:
@Override
    @Transactional(propagation = Propagation.NESTED,rollbackFor = Exception.class)
    public void testNested(){
        throw new RuntimeException();
    }

注意:外部方法异常,导致回滚也会导致内部方法回滚;因为父事务结束之前,子事务是不会提交的;子事务是父事务的一部分,由父事务统一提交。

该事物的表现形式在功能上跟 PROPAGATION_REQUIRES_NEW 有点共同点:即内部异常不会导致导致外部的事物的回滚;但是 PROPAGATION_REQUIRES_NEW 外部事物异常不会导致内部事物的回滚,而 PROPAGATION_NESTED 不一样,外部的事物回滚一样会导致内部子事物的回滚


参考:https://www.byteslounge.com/tutorials/spring-transaction-propagation-tutorial

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值