前提
以下案例使用成功的前提是Spring事务传播机制正常工作的情况下。 个人理解Spring传播机制一定要自己写Demo测试复现以方便理解记忆,避免在企业项目开发中踩坑。 博客错误及不足之处欢迎评论指正,谢谢!
Propagation.REQUIRED
默认事务,如果不存在事务,创建一个事务,然后执行事务操作,最常见的选择。
该事务的行为:
- 如果它作为一个子事务方法,在其他事务方法中被调用,那么该方法不会创建新的事务,加入当前事务,使用现有的父级别的事务。
- 如果它作为一个子事务方法,没有在其他事务方法中被调用,而是在非事务方法中直接调用,那么它会创建一个新的事务来执行数据库操作。
应用场景:
不知道方法的调用者是否创建了事务,但是要求当前被调用的方法必须在一个事务中执行。
注意:当需要日志记录的业务场景中,外部事务记录日志信息留痕,并在外部代码中捕获异常处理,主流业务使用单独方法,传播行为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();
}
}
- 内部事务异常,外部事务未捕获Service异常时,内部Service异常时,不影响外部事务,仅回滚内部事务。
- 内部事务异常,外部事务捕获Service异常时,内部Service异常时,影响内部事务,两事务均回滚。
- 外部事务异常,在调用内部事务前,外部事务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异常 | 回滚 | 回滚 |
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是同一个。