1. 概述

本文深入解析 Spring 中 @Transactional 注解的核心配置项:事务传播(Propagation)隔离级别(Isolation)。这两个属性直接影响事务的行为和并发控制,是实际开发中必须掌握的要点,稍有不慎就容易踩坑。

2. @Transactional 注解基础

@Transactional 是 Spring 声明式事务管理的核心注解,用于将一个方法的执行包裹在一个数据库事务中。通过它可以精细控制事务的:

  • 传播行为(Propagation)
  • 隔离级别(Isolation)
  • 超时时间(Timeout)
  • 是否只读(Read-only)
  • 回滚规则(Rollback rules)
  • 使用的事务管理器(Transaction Manager)

2.1 实现原理简述

Spring 通过 动态代理(AOP)或 字节码增强(如 AspectJ)来实现事务管理。在目标方法执行前后,自动织入事务的开启、提交或回滚逻辑。

✅ 关键点:内部方法调用会绕过代理,导致 @Transactional 失效。例如,类内一个非事务方法调用本类的 @Transactional 方法,事务不会生效。

其底层逻辑可简化为:

createTransactionIfNecessary();
try {
    callMethod();
    commitTransactionAfterReturning();
} catch (exception) {
    completeTransactionAfterThrowing();
    throw exception;
}

2.2 使用方式与优先级

@Transactional 可标注在:

  • 接口定义上
  • 类定义上
  • 具体方法上

⚠️ 优先级顺序(由低到高):
接口 < 父类 < 类 < 接口方法 < 父类方法 < 类方法

Spring 会将类级别的 @Transactional 自动应用到该类所有未显式标注的 public 方法上。

❌ 但若将注解加在 privateprotected 方法上,Spring 会静默忽略,不会报错,这是个经典陷阱。

示例:接口与类上的使用

@Transactional
public interface TransferService {
    void transfer(String user1, String user2, double val);
}

💡 虽然技术上可行,但通常不推荐在接口上使用 @Transactional,除非是像 Spring Data JPA 的 Repository 这种特殊场景。

覆盖接口配置:

@Service
@Transactional
public class TransferServiceImpl implements TransferService {
    @Override
    public void transfer(String user1, String user2, double val) {
        // ...
    }
}

进一步在方法级别覆盖:

@Transactional
public void transfer(String user1, String user2, double val) {
    // ...
}

3. 事务传播行为(Propagation)

传播行为定义了当前事务方法被调用时,如何与已存在的事务进行交互。它是控制事务边界的“粘合剂”。

Spring 通过 TransactionManager::getTransaction() 方法根据传播设置来获取或创建事务。

3.1 REQUIRED(默认)

  • 无事务则创建新事务
  • 有事务则加入当前事务

这是最常用的传播行为,也是默认值。

@Transactional(propagation = Propagation.REQUIRED)
public void requiredExample(String user) { 
    // ... 
}

由于是默认值,可简写为:

@Transactional
public void requiredExample(String user) { 
    // ... 
}

伪代码逻辑:

if (isExistingTransaction()) {
    if (isValidateExistingTransaction()) {
        validateExisitingAndThrowExceptionIfNotValid();
    }
    return existing;
}
return createNewTransaction();

3.2 SUPPORTS

  • 有事务则加入
  • 无事务则以非事务方式执行

适用于那些“有事务更好,没有也行”的操作,比如某些查询。

@Transactional(propagation = Propagation.SUPPORTS)
public void supportsExample(String user) { 
    // ... 
}

伪代码:

if (isExistingTransaction()) {
    if (isValidateExistingTransaction()) {
        validateExisitingAndThrowExceptionIfNotValid();
    }
    return existing;
}
return emptyTransaction;

3.3 MANDATORY

  • 必须有事务,否则抛异常

适用于那些只能在事务上下文中执行的操作。

@Transactional(propagation = Propagation.MANDATORY)
public void mandatoryExample(String user) { 
    // ... 
}

伪代码:

if (isExistingTransaction()) {
    if (isValidateExistingTransaction()) {
        validateExisitingAndThrowExceptionIfNotValid();
    }
    return existing;
}
throw IllegalTransactionStateException;

3.4 NEVER

  • 绝对不能有事务,否则抛异常

用于确保某些操作在非事务环境下执行。

@Transactional(propagation = Propagation.NEVER)
public void neverExample(String user) { 
    // ... 
}

伪代码:

if (isExistingTransaction()) {
    throw IllegalTransactionStateException;
}
return emptyTransaction;

3.5 NOT_SUPPORTED

  • 有事务则挂起当前事务
  • 以非事务方式执行业务逻辑

常用于执行一些与当前事务无关、且可能耗时的操作(如发送邮件),避免其影响主事务。

@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void notSupportedExample(String user) { 
    // ... 
}

⚠️ 注意:JTATransactionManager 支持真正的事务挂起。其他事务管理器(如 DataSourceTransactionManager)是通过清除线程上下文中的事务信息来模拟挂起的。

3.6 REQUIRES_NEW

  • 总是创建一个新事务
  • 如果当前有事务,则先挂起它

新事务独立提交或回滚,不影响外层事务。常用于日志记录、审计等需要独立提交的场景。

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void requiresNewExample(String user) { 
    // ... 
}

⚠️ 同样,真正的挂起依赖 JTATransactionManager。在 DataSourceTransactionManager 下,外层事务的挂起是通过线程上下文切换实现的。

伪代码:

if (isExistingTransaction()) {
    suspend(existing);
    try {
        return createNewTransaction();
    } catch (exception) {
        resumeAfterBeginException();
        throw exception;
    }
}
return createNewTransaction();

3.7 NESTED

  • 有事务则创建一个嵌套事务(保存点)
  • 无事务则等同于 REQUIRED

嵌套事务是外层事务的一部分,回滚时只会回滚到保存点,不影响外层已执行的部分。但外层事务回滚会连带回滚所有嵌套事务。

@Transactional(propagation = Propagation.NESTED)
public void nestedExample(String user) { 
    // ... 
}

支持情况:

  • DataSourceTransactionManager:原生支持。
  • JpaTransactionManager:仅当 JDBC 驱动支持保存点(Savepoints)且设置了 nestedTransactionAllowed=true 时才支持。
  • 部分 JTATransactionManager 实现支持。

4. 事务隔离级别(Isolation)

隔离是 ACID 中的 "I",决定了并发事务之间的可见性,防止脏读、不可重复读、幻读等问题。

4.1 Spring 中的隔离管理

Spring 通过 @Transactional::isolation 设置隔离级别,支持以下枚举:

  • DEFAULT
  • READ_UNCOMMITTED
  • READ_COMMITTED
  • REPEATABLE_READ
  • SERIALIZABLE

DEFAULT 表示使用底层数据库的默认隔离级别。切换数据库时需特别注意,不同数据库默认值不同。

⚠️ 当调用链中方法设置了不同隔离级别时,Spring 默认只在创建新事务时才应用隔离设置。若想严格校验现有事务的隔离级别是否匹配,需设置 TransactionManager::setValidateExistingTransaction(true)

校验伪代码:

if (isolationLevel != ISOLATION_DEFAULT) {
    if (currentTransactionIsolationLevel() != isolationLevel) {
        throw IllegalTransactionStateException
    }
}

4.2 READ_UNCOMMITTED(读未提交)

  • ❌ 允许脏读(Dirty Read)
  • ❌ 允许不可重复读(Non-repeatable Read)
  • ❌ 允许幻读(Phantom Read)

最低隔离级别,性能最高,但数据一致性最差。

@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void log(String message) {
    // ...
}

⚠️ PostgreSQL 和 Oracle 不支持此级别,PostgreSQL 会自动降级为 READ_COMMITTED

4.3 READ_COMMITTED(读已提交)

  • ✅ 防止脏读
  • ❌ 允许不可重复读
  • ❌ 允许幻读

大多数主流数据库(PostgreSQL、SQL Server、Oracle)的默认隔离级别

@Transactional(isolation = Isolation.READ_COMMITTED)
public void log(String message){
    // ...
}

4.4 REPEATABLE_READ(可重复读)

  • ✅ 防止脏读
  • ✅ 防止不可重复读
  • ❌ 允许幻读(MySQL InnoDB 通过间隙锁解决了幻读,行为上接近串行化)

MySQL 的默认隔离级别

此级别能有效防止“丢失更新”(Lost Update)问题。

@Transactional(isolation = Isolation.REPEATABLE_READ) 
public void log(String message){
    // ...
}

⚠️ Oracle 不支持 REPEATABLE_READ

4.5 SERIALIZABLE(串行化)

  • ✅ 防止所有并发问题(脏读、不可重复读、幻读)

最高隔离级别,通过强制事务串行执行来保证一致性,但并发性能最差。

@Transactional(isolation = Isolation.SERIALIZABLE)
public void log(String message){
    // ...
}

5. 总结

本文系统梳理了 @Transactional 的传播行为与隔离级别,这是 Spring 事务管理的两大核心支柱。

  • 传播行为决定事务的“边界合并策略”。
  • 隔离级别决定并发访问的“数据可见性规则”。

实际开发中,REQUIRED + READ_COMMITTED 是最常见的组合。深入理解这些机制,能有效避免数据不一致、死锁、性能瓶颈等问题。

💡 完整示例代码可参考 GitHub 仓库:https://github.com/tech-tutorial/spring-transaction-demo


原始标题:Transaction Propagation and Isolation in Spring @Transactional