第1篇-从Transactional到MySQL返回的完整旅程
约 2868 字大约 10 分钟
2026-05-24
3. 第三步:调用目标 Service 方法
如果没有异常,TransactionInterceptor 会调用目标 Service 方法(比如 userService.createUser())。
在 Service 方法中,你可能会调用 UserMapper.insertUser()。
4. 第四步:MapperProxy.invoke()(Mapper 代理对象)
当你调用 UserMapper.insertUser() 时,实际上调用的是 MapperProxy 代理对象。
核心源码(MapperProxy.invoke()):
// 类路径:org.apache.ibatis.binding.MapperProxy
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else {
// 【核心】创建 MapperMethod 对象,然后调用 execute() 方法
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}核心源码(MapperMethod.execute()):
// 类路径:org.apache.ibatis.binding.MapperMethod
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
case INSERT: {
// 【核心】处理 INSERT 语句
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
// 【核心】处理 UPDATE 语句
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
// 【核心】处理 DELETE 语句
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
case SELECT: {
// 【核心】处理 SELECT 语句
// ... 省略 ...
break;
}
default:
throw new BindingException("Unknown execution method for: " + command.getName());
}
return result;
}关键点:
- 判断 SQL 类型:INSERT/UPDATE/DELETE/SELECT
- 转换参数:把方法参数转换成 SQL 参数
- 调用 SqlSession 的对应方法:比如
sqlSession.insert()、sqlSession.select()等
5. 第五步:SqlSessionTemplate.insert/update/delete/select()
MapperMethod 会调用 SqlSessionTemplate 的对应方法。
你可能会问:为什么是 SqlSessionTemplate?
答案:因为 SqlSessionTemplate 是 Spring 整合的 SqlSession,它保证了 SqlSession 和 Spring 事务的同步!
核心源码(SqlSessionTemplate.insert()):
// 类路径:org.mybatis.spring.SqlSessionTemplate
@Override
public int insert(String statement, Object parameter) {
// 【核心】获取 SqlSession(从 ThreadLocal 中获取,或者创建新的)
SqlSession sqlSession = getSqlSession();
try {
// 【核心】调用 DefaultSqlSession.insert()
return sqlSession.insert(statement, parameter);
} finally {
// 【核心】关闭 SqlSession(如果是 Spring 管理的事务,不会真正关闭)
closeSqlSession(sqlSession);
}
}关键点:
- 获取 SqlSession:从
ThreadLocal中获取(如果当前线程已经绑定了SqlSession,直接返回;否则,创建新的) - 调用 DefaultSqlSession.insert():实际的
SqlSession实现 - 关闭 SqlSession:如果是 Spring 管理的事务,不会真正关闭(只是减少引用计数)
6. 第六步:Executor.update/query()(执行器)
DefaultSqlSession 会调用 Executor 的对应方法。
核心源码(SimpleExecutor.doUpdate()):
// 类路径:org.apache.ibatis.executor.SimpleExecutor
@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
// 【核心】第一步:创建 StatementHandler
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
// 【核心】第二步:准备 Statement(创建 PreparedStatement、设置参数)
stmt = prepareStatement(handler, ms.getStatementLog());
// 【核心】第三步:执行更新(调用 PreparedStatementHandler.update())
return handler.update(stmt);
} finally {
closeStatement(stmt);
}
}关键点:
- 创建 StatementHandler:比如
PreparedStatementHandler - 准备 Statement:创建
PreparedStatement、设置参数 - 执行更新:调用
PreparedStatementHandler.update()
7. 第七步:StatementHandler.prepare()(创建 Statement)
SimpleExecutor.prepareStatement() 会调用 StatementHandler.prepare() 方法。
核心源码(BaseStatementHandler.prepare()):
// 类路径:org.apache.ibatis.executor.statement.BaseStatementHandler
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
// 【核心】第一步:实例化 Statement(由子类实现)
// 比如 PreparedStatementHandler.instantiateStatement()
// 会调用 connection.prepareStatement(sql)
statement = instantiateStatement(connection);
// 【核心】第二步:设置超时时间
setStatementTimeout(statement, transactionTimeout);
// 【核心】第三步:设置 FetchSize
setFetchSize(statement);
return statement;
} catch (SQLException e) {
closeStatement(statement);
throw e;
}
}关键点:
- 创建 PreparedStatement:调用
connection.prepareStatement(sql) - 设置超时时间:
stmt.setQueryTimeout() - 设置 FetchSize:
stmt.setFetchSize()(控制每次从数据库读取的行数)
8. 第八步:StatementHandler.parameterize()(设置参数)
创建 PreparedStatement 之后,需要设置 SQL 参数(比如 #{name}、#{age})。
核心源码(DefaultParameterHandler.setParameters()):
// 类路径:org.apache.ibatis.scripting.defaults.DefaultParameterHandler
@Override
public void setParameters(PreparedStatement ps) {
ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
// 【核心】获取参数映射列表(比如:#{name} -> name, #{age} -> age)
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
if (parameterMappings != null) {
MetaObject metaObject = null;
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
// 【核心】获取参数值(从 Java 对象中获取)
if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
if (metaObject == null) {
metaObject = configuration.newMetaObject(parameterObject);
}
value = metaObject.getValue(propertyName);
}
// 【核心】使用 TypeHandler 设置参数到 PreparedStatement
TypeHandler typeHandler = parameterMapping.getTypeHandler();
JdbcType jdbcType = parameterMapping.getJdbcType();
typeHandler.setParameter(ps, i + 1, value, jdbcType);
}
}
}
}关键点:
- 获取参数值:从 Java 对象中获取(比如
user.getName()) - 使用 TypeHandler 设置参数:比如
ps.setString(1, "张三")``ps.setInt(2, 25)
9. 第九步:StatementHandler.update/query()(执行 SQL)
设置参数之后,就可以执行 SQL 了!
核心源码(PreparedStatementHandler.update()):
// 类路径:org.apache.ibatis.executor.statement.PreparedStatementHandler
@Override
public int update(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
// 【核心】执行 SQL!
ps.execute();
// 【核心】获取影响行数
int rows = ps.getUpdateCount();
Object parameterObject = boundSql.getParameterObject();
KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
// 【核心】处理主键生成(比如自增 ID)
keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
return rows;
}核心源码(PreparedStatementHandler.query()):
// 类路径:org.apache.ibatis.executor.statement.PreparedStatementHandler
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
// 【核心】执行 SQL!
ps.execute();
// 【核心】处理结果集!
return resultSetHandler.handleResultSets(ps);
}关键点:
- 执行 SQL:调用
PreparedStatement.execute() - 处理结果集:调用
DefaultResultSetHandler.handleResultSets()
10. 第十步:Druid 连接池(获取连接的过程)
你可能会问:连接是从哪里来的?
答案:从 Druid 连接池中来的!
当你调用 DataSourceUtils.getConnection() 时,最终会调用 DruidDataSource.getConnection()。
核心源码(DruidDataSource.getConnection()):
// 类路径:com.alibaba.druid.pool.DruidDataSource
@Override
public DruidPooledConnection getConnection() throws SQLException {
// 【核心】获取连接(最长等待 timeOut 毫秒)
return getConnection(maxWait);
}
public DruidPooledConnection getConnection(long maxWaitMillis) throws SQLException {
// 【核心】初始化(如果还没有初始化,就初始化连接池)
init();
// 【核心】从连接池中获取连接
DruidPooledConnection poolableConnection = getConnectionInternal(maxWaitMillis);
// 【核心】执行 Filter 链(比如监控统计、SQL 防火墙等)
if (proxyFilters.size() > 0) {
return new DruidPooledConnection(poolableConnection.getConnection(), this, proxyFilters);
}
return poolableConnection;
}关键点:
- 初始化连接池:如果还没有初始化,就创建初始连接
- 从连接池中获取连接:如果有空闲连接,直接返回;否则,创建新的连接(如果不超过最大连接数)
- 执行 Filter 链:比如监控统计、SQL 防火墙等
11. 第十一步:MySQL 驱动(JDBC 实现)
你可能会问:SQL 是如何发送到 MySQL 服务器的?
答案:通过 MySQL 驱动(JDBC 实现)!
当你调用 PreparedStatement.execute() 时,最终会调用 MySQL PreparedStatement.execute()。
核心流程:
- 发送 SQL 到 MySQL 服务器:通过 TCP/IP 连接发送
- 接收 MySQL 返回的结果:读取 TCP/IP 响应
- 解析结果:把 MySQL 返回的数据转换成
ResultSet
12. 第十二步:处理结果集(如果是查询)
如果是查询操作,需要把 ResultSet 转换成 Java 对象。
核心源码(DefaultResultSetHandler.handleResultSets()):
// 类路径:org.apache.ibatis.executor.resultset.DefaultResultSetHandler
@Override
public List<Object> handleResultSets(Statement stmt) throws SQLException {
// 【核心】获取第一个 ResultSet
ResultSetWrapper rsw = getFirstResultSet(stmt);
// 【核心】获取 ResultMap(结果映射)
List<ResultMap> resultMaps = mappedStatement.getResultMaps();
int resultMapCount = resultMaps.size();
// 【核心】处理所有结果集(存储过程可能返回多个结果集)
while (rsw != null && resultMapCount > resultSetCount) {
ResultMap resultMap = resultMaps.get(resultSetCount);
handleResultSet(rsw, resultMap, multipleResults, null);
rsw = getNextResultSet(stmt);
cleanUpAfterHandlingResultSet();
resultSetCount++;
}
return collapseSingleResultList(multipleResults);
}关键点:
- 获取 ResultSet:从
PreparedStatement中获取 - 处理结果集:把每一行数据转换成 Java 对象(比如
User) - 自动映射:数据库列名
user_name-> Java 属性名userName(如果开启了mapUnderscoreToCamelCase) - 属性映射:根据
ResultMap的配置,设置属性值
13. 第十三步:事务提交/回滚
如果没有异常,Spring 会提交事务;如果有异常,根据回滚规则决定是提交还是回滚。
核心源码(DataSourceTransactionManager.doCommit()):
// 类路径:org.springframework.jdbc.datasource.DataSourceTransactionManager
@Override
protected void doCommit(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
try {
// 【核心】提交事务!
con.commit();
} catch (SQLException ex) {
throw new TransactionSystemException("Could not commit JDBC transaction", ex);
}
}核心源码(DataSourceTransactionManager.doRollback()):
// 类路径:org.springframework.jdbc.datasource.DataSourceTransactionManager
@Override
protected void doRollback(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
try {
// 【核心】回滚事务!
con.rollback();
} catch (SQLException ex) {
throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
}
}关键点:
- 提交事务:调用
connection.commit() - 回滚事务:调用
connection.rollback()
14. 第十四步:释放连接
事务提交或回滚之后,需要释放连接。
核心源码(DataSourceUtils.releaseConnection()):
// 类路径:org.springframework.jdbc.datasource.DataSourceUtils
public static void releaseConnection(@Nullable Connection con, DataSource dataSource) throws SQLException {
if (con == null) {
return;
}
// 【核心】如果当前有事务,不真正关闭连接,只是重置连接状态
if (TransactionSynchronizationManager.isActive()) {
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
if (conHolder != null && connectionEquals(conHolder, con)) {
// 【注意】不真正关闭连接,只是减少引用计数
conHolder.released();
return;
}
}
// 【核心】如果当前没有事务,真正关闭连接(或者归还到连接池)
doCloseConnection(con, dataSource);
}关键点:
- 如果当前有事务:不真正关闭连接,只是减少引用计数(因为事务可能还没结束)
- 如果当前没有事务:真正关闭连接(或者归还到连接池)
三、关键源码速查表
为了方便你查阅,我整理了一个关键源码速查表:
| 序号 | 组件 | 核心类 | 核心方法 | 作用 |
|---|---|---|---|---|
| 1 | AOP 代理 | TransactionInterceptor | invoke() | 拦截 @Transactional 方法 |
| 2 | 事务属性 | AbstractFallbackTransactionAttributeSource | computeTransactionAttribute() | 获取事务属性 |
| 3 | 事务管理器 | AbstractPlatformTransactionManager | getTransaction() | 获取事务 |
| 4 | 事务管理器 | DataSourceTransactionManager | doBegin() | 开始事务(获取连接、设置手动提交、绑定到线程) |
| 5 | 连接工具 | DataSourceUtils | getConnection() | 从数据源获取连接 |
| 6 | 事务同步管理器 | TransactionSynchronizationManager | bindResource() | 绑定资源到当前线程 |
| 7 | Mapper 代理 | MapperProxy | invoke() | 代理 Mapper 接口方法 |
| 8 | Mapper 方法 | MapperMethod | execute() | 判断 SQL 类型,调用 SqlSession |
| 9 | SqlSession | SqlSessionTemplate | insert/update/delete/select() | Spring 整合的 SqlSession |
| 10 | 执行器 | SimpleExecutor | doUpdate/doQuery() | 执行 SQL |
| 11 | Statement 处理器 | PreparedStatementHandler | prepare/update/query() | 创建 Statement、设置参数、执行 SQL |
| 12 | 参数处理器 | DefaultParameterHandler | setParameters() | 设置参数到 PreparedStatement |
| 13 | 结果集处理器 | DefaultResultSetHandler | handleResultSets() | 处理结果集,转换成 Java 对象 |
| 14 | 连接池 | DruidDataSource | getConnection() | 从连接池获取连接 |
| 15 | MySQL 驱动 | ConnectionImpl | prepareStatement() | 创建 PreparedStatement |
| 16 | 事务提交/回滚 | DataSourceTransactionManager | doCommit/doRollback() | 提交或回滚事务 |
| 17 | 连接释放 | DataSourceUtils | releaseConnection() | 释放连接(或归还到连接池) |
看到了吗? 这 17 个关键类,覆盖了从 @Transactional 到 MySQL 返回的完整链路!
四、总结与下一篇预告
1. 总结
今天,我们详细剖析了从 @Transactional 到 MySQL 返回的完整旅程,包括:
- 全链路时序图:展示了 14 个关键步骤
- 全链路详细剖析:一步一步讲解每个环节都发生了什么
- 关键源码速查表:方便你快速查阅
你可能会问:我知道了全链路,但是如何排查问题呢?比如:
- 事务不回滚?
- 连接泄漏?
- SQL 执行超时?
- MyBatis 查询结果不对?
别着急!在接下来的文章中,我们会详细讲解:
- 异常全景图:每个环节都可能出什么错?如何排查?
- 基于源码的调优实践:事务调优、数据源调优、MyBatis 调优
- 那些年我们踩过的事务坑:自调用、异常不回滚、连接泄漏等
2. 下一篇预告
在下一篇(系列四第 2 篇)中,我们会讲解 《异常全景图 —— 每个环节都可能出什么错?》。
你会学到:
- Spring 事务层面的异常(比如事务不回滚、自调用问题)
- 数据源层面的异常(比如连接泄漏、连接池耗尽)
- MyBatis 层面的异常(比如 SQL 执行失败、结果集映射错误)
- MySQL 层面的异常(比如死锁、超时)
你可能会问:为什么需要讲异常?
问得好! 因为在实际工作中,出问题才是常态!你需要:
- 快速定位问题:知道每个环节可能出什么错
- 快速解决问题:知道如何排查、如何修复
所以,下一篇文章会是非常实用、非常有收获的!相信我!😎
写到这里,我不禁感叹:从 @Transactional 到 MySQL 返回,竟然经历了这么多步骤!但是,每个步骤都有它的职责,每个组件都有它的价值。
这就是软件系统的复杂性,也是软件系统的魅力!
好了,今天的文章就到这里。如果你觉得有收获,别忘了点赞、收藏、关注哦!你的支持,是我创作的最大动力!❤️
我们下一篇见!👋