bigywater.tech
Spring · 34

TransactionSynchronizationManager:事务资源绑在线程上

事务状态存在哪里?提交前后各个回调谁先谁后?afterCommit 里抛异常会怎样?为什么子线程、自己拿的连接都不在事务里?这篇在 Spring Framework 7.0.9 和 MySQL 26.7.0 上逐项验证。

1 个
事务线程上绑定的资源数(HikariDataSource → ConnectionHolder);子线程里是 0 个
16 对 15
直接 ds.getConnection() 与事务使用的连接 id,写入不会随事务回滚
2 行
afterCommit 里抛异常之后表里仍然存在的行数(提交已经发生)

线程上的资源

实测 · 本机执行输出
  --- @Transactional(isolation = READ_COMMITTED) 方法里
  1. isSynchronizationActive=true,isActualTransactionActive=true
  2. 事务名=Svc.inspect,隔离级别=2(2 = READ_COMMITTED),只读=false
  3. 线程上绑定的资源:键 HikariDataSource → 值 ConnectionHolder,持有连接 HikariProxyConnection
  4. 已注册的同步器数:0(JdbcTemplate 之类的在需要时注册)
  --- 事务外
  1. 事务外 isSynchronizationActive=false,资源 0 个

事务名、隔离级别、只读标志和 ConnectionHolder 都绑在当前线程上。

回调顺序

实测 · 本机执行输出
  --- 正常提交
  1. 方法体里
  2. beforeCommit(readOnly=false)
  3. beforeCompletion()
  4. afterCommit():事务已经提交了,但连接还没还给连接池
  5. afterCompletion(status=COMMITTED)
  --- 回滚
  1. 方法体里
  2. beforeCompletion()
  3. afterCompletion(status=ROLLED_BACK)
  4. 调用者收到 IllegalStateException
  --- afterCommit 里抛异常(方法本身成功)
  1. 方法体里
  2. beforeCommit(readOnly=false)
  3. beforeCompletion()
  4. afterCommit():事务已经提交了,但连接还没还给连接池
  5. afterCompletion(status=COMMITTED)
  6. 调用者收到 IllegalStateException: afterCommit 里抛异常;表里行数 2

提交与回滚的回调不同,afterCommit 抛异常撤销不了提交。

换线程

实测 · 本机执行输出
  --- 事务线程与子线程
  1. 主线程 isActualTransactionActive=true,资源 1 个;子线程 isActualTransactionActive=false,资源 0 个

子线程里事务不活跃,没有资源。

绕过 Spring 拿连接

实测 · 本机执行输出
  --- 在 @Transactional 里自己拿连接写入
  1. 直接 ds.getConnection():得到的连接 id 16,事务连接 id 15(不同的连接)
  2. 方法抛了异常之后,表里有 1 行(0 表示 insert 跟着回滚了)
  3. 经 TransactionAwareDataSourceProxy:得到的连接 id 15,事务连接 id 15(同一个连接)
  4. 方法抛了异常之后,表里有 0 行(0 表示 insert 跟着回滚了)

直接 ds.getConnection() 不在事务里,TransactionAwareDataSourceProxy 能让它加入。

分步演示

TransactionSynchronizationManager
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK 21.0.12(Homebrew) / Spring Boot 4.0.8(Spring Framework 7.0.9、Tomcat 11.0.24) / Spring Cloud 2025.1.3;用 javac 编译、java 直接运行,依赖由 Maven 从 Maven Central 解析;数字每次运行会有小幅抖动。用不带 Boot 的 Spring Framework 7.0.9 + DataSourceTransactionManager + HikariCP + 自己起的 MySQL 26.7.0;连接 id 是 MySQL 的 connection_id(),每次运行的具体值不同(这次是 15、16)
    • 没有验证:TransactionSynchronization 的顺序(@Order / Ordered);registerSynchronization 在没有事务时抛 IllegalStateException;afterCommit 里再开新事务(要 REQUIRES_NEW);响应式事务的 TransactionContext;JTA 下的 TransactionSynchronizationRegistry
    京ICP备2026067502号-1京公网安备11010502063487号