一文搞懂Spring事务传播机制
@Transactional默认的异常回滚和事务传播行为
我们在使用Spring管理数据库事务的时候很方便,只需要在代理对象中引入注解@Transactional
就可以开启事务了。我们在使用@Transactional
,一般主要关心两个方面,一个是异常回滚的定义(设置rollbackFor
),另一个一个是事务传播行为的定义(设置propagation
)。
默认情况下,在发生 RuntimeException
和Error
时会回滚。默认的事务传播行为是支持当前的事务,如果不存在事务,则创建个新的事务。以上的默认行为可以覆盖较大一部分的应用场景, 使用起来非常方便,不用配置参数就可以适应较大一部分的应用场景。但是涉及到2个以上的服务使用事务的时候,还需要了解更多的事务传播行为。
Spring中定义的事务传播行为
Spring中在org.springframework.transaction.TransactionDefinition
中定义的事务传播行为,一共有7种行为,可以按支持当前事务与否分成三类。
- 支持当前事务
PROPAGATION_REQUIRED
: 如果当前存在事务,则使用该事务。 如果当前没有事务,则创建一个新的事务。PROPAGATION_SUPPORTS
: 如果当前存在事务,则使用该事务,如果当前不存在事务,则仍以非事务的方式运行。PROPAGATION_MANDATORY
: 如果当前存在事务,则使用该事务,如果当前事务不存在则抛出异常
- 不支持当前事务
PROPAGATION_REQUIRES_NEW
: 总是创建一个新的事务,如果当前有事务,则将当前事务挂起PROPAGATION_NOT_SUPPORTED
:非事务的方式运行,如果当前有事务,则将当前事务挂起PROPAGATION_NEVER
: 以事务的方式运行,如果当前有事务,则抛出异常
- 嵌套事务
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
使用嵌套事务的场景有两点需求:
- 联合成功:需要上游事务与下游事务一起提交,即:下游事务只有在上游事务成功提交时才提交。这一点
PROPAGATION_REQUIRED
可以做到。 - 隔离失败:需要下游事务回滚不影响上游事务的提交。这个需求简单称之为“隔离失败”。这一点
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