bigywater.tech
Spring · 29

@Transactional 的实现:TransactionInterceptor 到 PlatformTransactionManager

@Transactional 到底做了什么?事务是怎么和一个连接绑在一起的?MySQL 那边能看到什么?事务状态放在哪里?这篇用 Spring Framework 7.0.9、HikariCP 和自己起的 MySQL 26.7.0 把一次事务的全过程逐步量出来。

7 条
一次 @Transactional 方法在执行它的那个连接上留下的 MySQL 语句数(含 SET autocommit=0 与 COMMIT)
同一个连接
事务内两条 insert 之间 connection_id() 都是 12
0 行
业务异常回滚的那次留在表里的行数

管理器的调用顺序

实测 · 本机执行输出
  --- 事务管理器被调用的过程(正常提交)
  1.   doGetTransaction()
  2.   doBegin(Svc.place,传播 0,只读 false):从连接池拿连接、setAutoCommit(false)、把连接绑定到当前线程
  3. 进入 place():事务活跃=true,事务名=Svc.place,绑定的资源=[HikariDataSource]
  4. 两条 insert 之间 connection_id():12 和 12(同一个连接)
  5.   doCommit():connection.commit()
  6.   doCleanupAfterCompletion():解除绑定、恢复 autoCommit、把连接还给连接池
  --- 事务管理器被调用的过程(业务异常回滚)
  1.   doGetTransaction()
  2.   doBegin(Svc.place,传播 0,只读 false):从连接池拿连接、setAutoCommit(false)、把连接绑定到当前线程
  3. 进入 place():事务活跃=true,事务名=Svc.place,绑定的资源=[HikariDataSource]
  4. 两条 insert 之间 connection_id():12 和 12(同一个连接)
  5.   doRollback():connection.rollback()
  6.   doCleanupAfterCompletion():解除绑定、恢复 autoCommit、把连接还给连接池
  7. 调用者收到 IllegalStateException
  两次调用之后表 t 里有 2 行(place(false) 提交了 2 行,place(true) 的 2 行回滚了)

doGetTransaction、doBegin、doCommit 或 doRollback、doCleanupAfterCompletion。

MySQL 看到的

实测 · 本机执行输出
  一次 @Transactional place(false) 在 MySQL general_log 里留下的语句(只取执行这个事务的那一个连接 thread_id;连接池后台新建的其它连接的握手语句略去):
    13 Query SET autocommit=0
    13 Query insert into t(v) values ('a')
    13 Query select connection_id()
    13 Query insert into t(v) values ('b')
    13 Query select connection_id()
    13 Query COMMIT
    13 Query SET autocommit=1
  没有 @Transactional 的 noTx()(两条 insert)留下的语句:
    13 Query insert into t(v) values ('x')
    13 Query insert into t(v) values ('y')

SET autocommit=0、业务语句、COMMIT、SET autocommit=1;没有 @Transactional 时每条语句自己提交。

事务状态

实测 · 本机执行输出
  事务外:isSynchronizationActive=false;isActualTransactionActive=false;资源 []
  --- noTx()
  1. 进入 noTx()(没有 @Transactional):事务活跃=false

放在线程上,事务外同步是关闭的、没有绑定资源。

分步演示

@Transactional 的实现
本步的对象

    本文用到的工具与边界

    • 本机 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 的 @EnableTransactionManagement + DataSourceTransactionManager(重写了几个方法加日志)+ HikariCP + 自己起的 MySQL 26.7.0(端口 9150 起,innodb);general_log 开在这个实例上(输出到表),只取执行事务的那个连接的语句;general_log 里的 thread_id 与 connection_id() 的值差 1(13 对 12),我没有查原因,没有当作同一个数用
    • 没有验证:JPA / Hibernate 的事务管理器(见后面的 Data 篇);JTA 事务;响应式事务管理器;事务内的连接复用在连接泄漏时的表现;Spring 的 TransactionInterceptor 属性解析细节;MySQL 26.7.0 以外的版本
    京ICP备2026067502号-1京公网安备11010502063487号