likes
comments
collection
share

一文搞懂Spring事务传播机制

作者站长头像
站长
· 阅读数 26

@Transactional默认的异常回滚和事务传播行为

我们在使用Spring管理数据库事务的时候很方便,只需要在代理对象中引入注解@Transactional 就可以开启事务了。我们在使用@Transactional,一般主要关心两个方面,一个是异常回滚的定义(设置rollbackFor),另一个一个是事务传播行为的定义(设置propagation)。 默认情况下,在发生 RuntimeExceptionError时会回滚。默认的事务传播行为是支持当前的事务,如果不存在事务,则创建个新的事务。以上的默认行为可以覆盖较大一部分的应用场景, 使用起来非常方便,不用配置参数就可以适应较大一部分的应用场景。但是涉及到2个以上的服务使用事务的时候,还需要了解更多的事务传播行为。

Spring中定义的事务传播行为

Spring中在org.springframework.transaction.TransactionDefinition中定义的事务传播行为,一共有7种行为,可以按支持当前事务与否分成三类。

  1. 支持当前事务
    1. PROPAGATION_REQUIRED : 如果当前存在事务,则使用该事务。 如果当前没有事务,则创建一个新的事务。
    2. PROPAGATION_SUPPORTS : 如果当前存在事务,则使用该事务,如果当前不存在事务,则仍以非事务的方式运行。
    3. PROPAGATION_MANDATORY : 如果当前存在事务,则使用该事务,如果当前事务不存在则抛出异常
  2. 不支持当前事务
    1. PROPAGATION_REQUIRES_NEW : 总是创建一个新的事务,如果当前有事务,则将当前事务挂起
    2. PROPAGATION_NOT_SUPPORTED :非事务的方式运行,如果当前有事务,则将当前事务挂起
    3. PROPAGATION_NEVER : 以事务的方式运行,如果当前有事务,则抛出异常
  3. 嵌套事务
    1. PROPAGATION_NESTED : 当前存在事务,则创建一个嵌套事务来运行,如果当前没有事务,行为等同于PROPAGATION_REQUIRED

以上就是事务7种传播行为的说明,这里单纯的用文字描述,会比较抽象,不太好理解,下面针对常见的三种事务传播行为REQUIRED ,REQUIRED_NEW ,NESTED的服务组合进行演示和说明。

常见事务传播行为的使用示例

1 上游服务REQUIRED,下游服务没添加事务

上游服务

    @Transactional(rollbackFor = Exception.class)
    public void createUser() {

        User user = new User();
        user.setAge(19).setName("test1").setVersion(1);
        userMapper.insert(user);

        logService.saveLog("createUser");
       
    }

下游服务抛出异常:

 public void saveLog(String method) {
        SystemLog entity  = new SystemLog();
        entity.setMethod(method);
        systemLogMapper.insert(entity);
        throw new RuntimeException("测试回滚 saveLog未添加事务,createUser Propagation.REQUIRED");

    }

则当前事务生效,当前服务createUser和下游服务saveLog都会回滚。

2 当前服务REQUIRED,下游服务REQUIRES_NEW

2.1 下游服务抛出异常

上游服务的代码不变,下游服务的代码修改如下

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveLog(String method) {
        SystemLog entity  = new SystemLog();
        entity.setMethod(method);
        systemLogMapper.insert(entity);
        throw new RuntimeException("测试回滚 saveLog Propagation.REQUIRES_NEW,createUser Propagation.REQUIRED");

    }

则当前事务生效,当前服务createUser和下游服务saveLog都会回滚。当前服务的事务被挂起,下游事务回滚,异常也会导致上游事务回滚。所以依然会回滚。

2.2 上游服务抛出异常

将抛出异常的操作放到上游服务,下游服务回复正常逻辑,修改代码如下

    @Transactional
    public void createUser() {

        User user = new User();
        user.setAge(19).setName("test1").setVersion(1);
        userMapper.insert(user);

        logService.saveLog("createUser");

        throw new RuntimeException("上游服务createUser Propagation.REQUIRED 抛出异常 " +
                "下游服务saveLog REQUIRES_NEW");

    }


    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveLog(String method) {
        SystemLog entity  = new SystemLog();
        entity.setMethod(method);
        systemLogMapper.insert(entity);
    }

上游服务createUser回滚,t_user中没有插入新的数据,下游服务 saveLog顺利执行 t_system_log中记录了一条新数据。原因是下游事务配置为REQUIRES_NEW 执行下游服务代码时,当前事务被挂起,下游事务执行,下游事务没有异常,顺利执行,返回到上游事务,上游事务遇到异常,上游发生回滚。

下游事务使用REQUIRES_NEW 则上下游是两个隔离的事务,下游的提交失败不会直接影响到上游事务,除非是第一种情况,下游服务抛出了异常 也会被抛到上游服务,上游服务也就被回滚了。

3 使用REQUIRES_NEST

使用嵌套事务的场景有两点需求:

  1. 联合成功:需要上游事务与下游事务一起提交,即:下游事务只有在上游事务成功提交时才提交。这一点PROPAGATION_REQUIRED可以做到。
  2. 隔离失败:需要下游事务回滚不影响上游事务的提交。这个需求简单称之为“隔离失败”。这一点PROPAGATION_REQUIRES_NEW可以做到。

如果使用PROPAGATION_REQUIRED满足需求1,但下游事务的回滚会无条件地使上游事务也回滚,不能满足需求2 下游事务提交失败不影响上游事务。

使用PROPAGATION_REQUIRES_NEW满足需求2,但下游事务是完全新的事务上下文,上游事务的成功提交与否完全不影响下游的提交,这不能满足需求1。

需要使用NESTED 才能满足上游影响下游,下游不影响上游

代码示例:

3.1 上游服务使用PROPAGATION_REQUIRED机制 抛出异常,下游服务使用NESTED机制

上游服务
    @Transactional
    public void createUser() {

        User user = new User();
        user.setAge(19).setName("test1").setVersion(5);
        userMapper.insert(user);

        logService.saveLog("createUser");
        throw new RuntimeException("上游抛出异常,下游NESTED机制");
    }
下游服务    
    @Transactional(propagation = Propagation.NESTED)
    public void saveLog(String method) {
        SystemLog entity  = new SystemLog();
        entity.setMethod(method);
        systemLogMapper.insert(entity);
    }

上游回滚了,下游也会回滚。

3.2 上游服务使用PROPAGATION_REQUIRED机制,下游服务使用NESTED机制 抛出异常

上游服务
    @Transactional
    public void createUser() {

        User user = new User();
        user.setAge(19).setName("test1").setVersion(5);
        userMapper.insert(user);
        try {
            logService.saveLog("createUser");
        }catch (Exception e) {

        }

    }
下游服务    
    @Transactional(propagation = Propagation.NESTED)
    public void saveLog(String method) {
        SystemLog entity  = new SystemLog();
        entity.setMethod(method);
        systemLogMapper.insert(entity);
        throw new RuntimeException("下游使用NESTED机制抛出异常");
    }

上游正常提交,下游回滚不影响上游提交。

总结

这里介绍了Spring事务的7种传播行为,共分为三类 支持当前事务的:PROPAGATION_REQUIRED,PROPAGATION_SUPPORTS,PROPAGATION_MANDATORY 不支持当前事务的:PROPAGATION_REQUIRES_NEW,PROPAGATION_NOT_SUPPORTED,PROPAGATION_NEVER,和嵌套事务:NESTED。 详细描述了最常用的三种PROPAGATION_REQUIRE PROPAGATION_REQUIRES_NEW NESTED传播行为

转载自:https://juejin.cn/post/6870790953922215950
评论
请登录