深入解析 Spring @Transactional 的事务开启机制
一、核心问题:无数据库操作时事务是否开启?
当方法被标记为 @Transactional
但内部没有数据库操作时,事务是否会被开启?
答案是:取决于事务管理器的实现和传播行为。以下分步骤解析其底层逻辑。
二、事务开启的底层流程
1. 代理对象的拦截机制
Spring 通过动态代理(JDK 动态代理或 CGLIB)为 @Transactional
方法生成代理对象。代理对象在方法调用时拦截请求,执行事务管理逻辑。
关键代码流程:
// 伪代码:代理对象的拦截逻辑
public Object intercept(Method method, Object[] args) {// 1. 检查方法是否标记为 @Transactionalif (isTransactional(method)) {// 2. 获取事务属性(传播行为、隔离级别等)TransactionAttribute txAttr = getTransactionAttribute(method);// 3. 通过事务管理器创建事务TransactionStatus status = transactionManager.getTransaction(txAttr);try {// 4. 执行原始方法Object result = method.invoke(target, args);// 5. 提交事务transactionManager.commit(status);return result;} catch (Exception ex) {// 6. 回滚事务transactionManager.rollback(status);throw ex;}} else {return method.invoke(target, args);}
}
2. 事务创建的触发条件
事务的创建由 transactionManager.getTransaction()
触发,而非数据库操作。
即使方法中没有数据库操作,只要满足以下条件,事务仍会被创建:
- 传播行为要求开启新事务(如
REQUIRES_NEW
)。 - 当前没有事务存在(如默认的
REQUIRED
行为)。
三、事务管理器的具体行为
1. JDBC 事务管理器(DataSourceTransactionManager)
- 行为:在
getTransaction()
时立即获取数据库连接,并开启事务。 - 示例:
日志输出:@Transactional public void emptyMethod() {// 无数据库操作 }
即使没有数据库操作,连接仍会被获取,事务会被提交。DEBUG o.s.j.d.DataSourceTransactionManager - Creating new transaction DEBUG o.s.j.d.DataSourceTransactionManager - Acquired Connection [conn1] for JDBC transaction DEBUG o.s.j.d.DataSourceTransactionManager - Committing JDBC transaction on Connection [conn1]
2. JPA 事务管理器(JpaTransactionManager)
- 行为:延迟获取数据库连接,直到实际执行数据库操作。
- 示例:
日志输出:@Transactional public void emptyMethod() {// 无数据库操作 }
事务会被创建,但未实际获取数据库连接(无DEBUG o.s.orm.jpa.JpaTransactionManager - Creating new transaction DEBUG o.s.orm.jpa.JpaTransactionManager - Committing JPA transaction
Connection
日志)。
四、传播行为的影响
1. REQUIRED(默认)
- 如果当前没有事务,开启新事务。
- 即使方法无数据库操作,事务仍会被创建(行为取决于事务管理器)。
2. REQUIRES_NEW
- 强制开启新事务,无论当前是否存在事务。
- 必定触发事务创建,无论方法内容如何。
3. SUPPORTS
- 如果当前存在事务,则加入;否则以非事务方式运行。
- 若无数据库操作且无现有事务,不会创建事务。
五、性能优化与验证
1. 如何验证事务是否开启?
- 开启 DEBUG 日志:
logging.level.org.springframework.transaction=DEBUG logging.level.org.springframework.jdbc=DEBUG logging.level.org.springframework.orm.jpa=DEBUG
- 观察日志中的事务生命周期事件(如
Creating new transaction
、Committing transaction
)。
2. 避免空事务的性能损耗
- 移除不必要的 @Transactional 注解。
- 使用
@Transactional(propagation = Propagation.NOT_SUPPORTED)
显式禁用事务。
六、总结:事务开启的决策逻辑
条件 | 事务是否开启? | 说明 |
---|---|---|
方法标记为 @Transactional | 是 | 由代理对象和事务管理器触发,与数据库操作无关。 |
传播行为为 REQUIRES_NEW | 是 | 强制开启新事务。 |
传播行为为 SUPPORTS | 否(若无现有事务) | 仅在存在事务时加入。 |
使用 JDBC 事务管理器 | 是(立即获取连接) | 即使无数据库操作,仍会获取连接并提交空事务。 |
使用 JPA 事务管理器 | 是(延迟获取连接) | 事务被创建,但未实际获取连接(无数据库操作时)。 |