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 方法上。
❌ 但若将注解加在 private
或 protected
方法上,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