事务传播行为

传播行为定义了当一个事务方法被另一个事务方法调用时,事务应该如何传播。在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("嵌套事务回滚");
    }
}

场景说明

嵌套事务使用保存点实现,内层回滚不影响外层。适用于需要部分回滚但不影响主流程的场景。注意:需要数据库支持保存点功能。


参考链接