事务传播行为
传播行为定义了当一个事务方法被另一个事务方法调用时,事务应该如何传播。在Spring中通过@Transactional(propagation = Propagation.XXX)来配置。
传播行为速查表
| 传播行为 | 当前有事务 | 当前无事务 | 典型场景 |
|---|---|---|---|
| REQUIRED(默认) | 加入当前事务 | 创建新事务 | 大多数业务场景 |
| REQUIRES_NEW | 挂起原事务,创建独立新事务 | 创建新事务 | 日志记录、审计 |
| SUPPORTS | 加入当前事务 | 非事务执行 | 只读查询方法 |
| NOT_SUPPORTED | 挂起原事务,非事务执行 | 非事务执行 | 调用外部系统 |
| MANDATORY | 加入当前事务 | 抛出异常 | 核心业务逻辑 |
| NEVER | 抛出异常 | 非事务执行 | 明确禁止事务的操作 |
| NESTED | 创建嵌套事务(保存点) | 创建新事务 | 部分回滚场景 |
常用传播行为
REQUIRED(默认)
- 如果当前存在事务,则加入该事务
- 如果当前没有事务,则创建一个新事务
- 最常用的传播行为,适合大多数场景
// 外层方法开启事务
@Transactional(propagation = Propagation.REQUIRED)
public void outerMethod() {
// 数据库操作A
doA();
// 内层方法加入外层事务
innerService.innerMethod();
}
// 内层方法,默认REQUIRED,加入外层事务
@Transactional
public void innerMethod() {
// 数据库操作B
doB();
}场景说明
外层方法回滚时,内层方法的操作也会回滚,因为它们在同一个事务中。
REQUIRES_NEW
- 挂起当前事务(如果存在),创建一个新事务
- 新事务与原事务完全独立
- 适用于需要独立提交或回滚的操作(如日志记录)
// 外层事务
@Transactional
public void outerMethod() {
// 操作A
doA();
try {
// 内层方法创建独立事务
logService.recordLog();
} catch (Exception e) {
// 日志记录失败不影响主业务
}
// 如果这里抛出异常,操作A回滚,但日志已提交不受影响
}
// 日志记录,独立事务
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void recordLog() {
// 记录操作日志
saveLog();
}场景说明
内层事务独立提交,即使外层事务回滚,内层操作依然生效。常用于日志记录、审计等需要确保执行的场景。
SUPPORTS
- 如果当前存在事务,则加入该事务
- 如果当前没有事务,则以非事务方式执行
// 查询方法,根据调用者决定是否使用事务
@Transactional(propagation = Propagation.SUPPORTS)
public List<User> queryUsers(String keyword) {
// 查询操作,可参与事务也可独立执行
return userRepository.findByKeyword(keyword);
}场景说明
适用于只读查询方法,既可以被事务方法调用(参与事务),也可以独立调用(非事务)。
NOT_SUPPORTED
- 挂起当前事务(如果存在),以非事务方式执行
// 外层事务
@Transactional
public void outerMethod() {
// 操作A在事务中执行
doA();
// 调用非事务方法,外层事务被挂起
externalService.callExternalApi();
// 操作B恢复到外层事务
doB();
}
// 外部API调用,强制非事务
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void callExternalApi() {
// 调用外部系统,不参与事务
externalApiClient.sendRequest();
}场景说明
强制以非事务方式执行,常用于调用外部系统、发送消息等不需要事务的操作。
MANDATORY
- 必须在一个已存在的事务中执行
- 如果没有事务,抛出异常
// 核心业务逻辑,必须在事务中执行
@Transactional(propagation = Propagation.MANDATORY)
public void criticalOperation() {
// 关键数据更新,必须有事务保护
updateCriticalData();
}
// 调用方必须开启事务
@Transactional
public void businessService() {
// 此调用正常执行,因为存在事务
criticalService.criticalOperation();
}
// 无事务调用会抛出异常
public void badCaller() {
// 抛出IllegalTransactionStateException
criticalService.criticalOperation();
}场景说明
确保方法必须在事务上下文中执行,常用于核心业务逻辑的保护。
NEVER
- 必须以非事务方式执行
- 如果存在事务,抛出异常
// 非事务日志方法,不能在事务中调用
@Transactional(propagation = Propagation.NEVER)
public void writeLog(String message) {
// 写入日志文件或外部系统
logger.write(message);
}
// 错误调用:在事务中调用NEVER方法
@Transactional
public void badCaller() {
// 抛出IllegalTransactionStateException
logService.writeLog("transactional log");
}
// 正确调用:非事务调用
public void goodCaller() {
logService.writeLog("non-transactional log");
}场景说明
强制非事务执行,与MANDATORY相反。适用于明确不能在事务中执行的操作。
NESTED
- 如果当前存在事务,则创建一个嵌套事务
- 嵌套事务是外部事务的子事务,外部事务回滚会导致嵌套事务也回滚
- 嵌套事务回滚不会影响外部事务(需要数据库支持保存点)
// 外层事务
@Transactional
public void outerMethod() {
// 操作A
doA();
try {
// 嵌套事务执行
nestedService.nestedOperation();
} catch (Exception e) {
// 嵌套事务回滚,但外层事务继续
log.warn("嵌套操作失败,继续外层事务");
}
// 操作B继续执行
doB();
}
// 嵌套事务方法
@Transactional(propagation = Propagation.NESTED)
public void nestedOperation() {
// 保存点创建
// 操作C在嵌套事务中执行
doC();
if (someCondition) {
// 仅回滚嵌套事务部分
throw new RuntimeException("嵌套事务回滚");
}
}场景说明
嵌套事务使用保存点实现,内层回滚不影响外层。适用于需要部分回滚但不影响主流程的场景。注意:需要数据库支持保存点功能。