Spring常用事务传播特性

2022-10-27 15:12:08 浏览数 (2)

前提

以下案例使用成功的前提是Spring事务传播机制正常工作的情况下。 个人理解Spring传播机制一定要自己写Demo测试复现以方便理解记忆,避免在企业项目开发中踩坑。 博客错误及不足之处欢迎评论指正,谢谢!

Propagation.REQUIRED

默认事务,如果不存在事务,创建一个事务,然后执行事务操作,最常见的选择。

该事务的行为:

  1. 如果它作为一个子事务方法,在其他事务方法中被调用,那么该方法不会创建新的事务,加入当前事务,使用现有的父级别的事务。
  2. 如果它作为一个子事务方法,没有在其他事务方法中被调用,而是在非事务方法中直接调用,那么它会创建一个新的事务来执行数据库操作。

应用场景:

不知道方法的调用者是否创建了事务,但是要求当前被调用的方法必须在一个事务中执行。

注意:当需要日志记录的业务场景中,外部事务记录日志信息留痕,并在外部代码中捕获异常处理,主流业务使用单独方法,传播行为REQUIRES_NEW,可以保证在不关注主流业务的情况下日志被留痕。

Propagation.REQUIRES_NEW

新建事务,不依赖于环境的”内部“事务,这个事务将被完全提交回滚而不依赖于外部事务,它拥有自己的隔离范围,自己的锁,当内部事务开始执行时,外部事务将被挂起,内部事务结束时,外部事务将继续执行。

应用场景:

常用于日志记录或交易失败仍要留痕及时序控制,即事务步骤要求时序的情况。

代码语言:javascript复制
public boolean createUser(User user) {
    userMapper.insertUser(user);
    Menu menu = new Menu();
    menu.setId(99);
    menu.setPattern("11");
    menuService.insertMenuRequireNew(menu);
    return true;
}
代码语言:javascript复制
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void insertMenuRequireNew(Menu menu) {
    menuMapper.addMenu(menu);
    if (true) {
        throw new RuntimeException();
    }
}
  1. 内部事务异常,外部事务未捕获Service异常时,内部Service异常时,不影响外部事务,仅回滚内部事务。
  2. 内部事务异常,外部事务捕获Service异常时,内部Service异常时,影响内部事务,两事务均回滚。
  3. 外部事务异常,在调用内部事务前,外部事务Service抛出异常,外部事务回滚
Propagation.NESTED

Spring查询当前是否存在事务,如果已存在事务,创建一个保存点,即若代码逻辑抛出异常,代码回滚到保存点,如果没有活跃的事务,则作用和默认REQUIRED类型事务一致。

NESTED类型创建的事务,实则为字事务。

异常类型

Service 1

Service 2

user正常/menu正常

正常提交

正常提交

user异常/menu正常

回滚

未执行

user正常/menu异常

外部user调用menu时使用try/catch捕获,user正常提交。当外部user调用menu不使用try/catch捕获时,user、menu均回滚

回滚

user异常/menu异常

回滚

回滚

代码语言:javascript复制
Service 1
@Transactional(propagation = Propagation.REQUIRED)
public void createUserNestedOpen() {
    try {
        User user = new User(221, "NESTED");
        userMapper.insertUser(user);
        Menu menu = new Menu();
        menu.setId(99);
        menu.setPattern("11");
        menuService.insertMenu(menu);
    } catch (Exception e) {
        log.error(e.getMessage());
    }
}
代码语言:javascript复制
Service 2
@Transactional(propagation = Propagation.NESTED)
public void insertMenu(Menu menu) {
    menuMapper.addMenu(menu);
    if (true) {
        throw new RuntimeException();
    }
}
使用try/catch捕获内部事务的情况

可见事务id是同一个。

外部事务异常

0 人点赞