开篇:当我们使用 @Transactional
时,背后发生了什么?
在 SpringBoot + MyBatis
的项目中,只需在 Service
方法上添加@Transactional
注解,就能轻松实现事务管理。但这个过程中,Spring
如何与 MyBatis
协作?事务的提交 / 回滚究竟由谁执行?本文将基于spring-tx 5.3.23
和spring-boot-starter 2.2.2
版本,深入剖析从注解到数据库的完整链路。
在 JDBC
规范中,所有事务操作都由Connection
接口定义:
// java.sql.Connection接口核心方法
void setAutoCommit(boolean autoCommit) throws SQLException; // 开启/关闭自动提交
void commit() throws SQLException; // 提交事务
void rollback() throws SQLException; // 回滚事务
无论上层框架如何封装,最终执行事务提交 / 回滚的永远是 JDBC
的 Connection
对象。Spring
的事务管理,本质是对这些底层操作的封装与流程控制。
Spring
通过DataSourceTransactionManager
管理 Connection
的生命周期,关键流程如下:
DataSource
) 获取 Connection
connection.setAutoCommit(false)
MyBatis
使用该 Connection
执行 SQL
connection.commit()
或connection.rollback()
Connection
返回给连接池伪代码展示Spring
管理Connection
的核心逻辑:
// 伪代码展示Spring管理Connection的核心逻辑
try {
// 1. 从数据源获取Connection
Connection conn = dataSource.getConnection();
// 2. 关闭自动提交,开启事务
conn.setAutoCommit(false);
try {
// 3. 执行SQL操作(MyBatis使用此Connection)
userMapper.insert(user);
orderMapper.createOrder(order);
// 4. 提交事务
conn.commit();
} catch (Exception e) {
// 5. 异常时回滚事务
conn.rollback();
} finally {
// 6. 释放连接
conn.close(); // 实际由连接池管理
}
} catch (SQLException ex) {
throw new RuntimeException("数据库操作失败", ex);
}
TransactionInterceptor
是 Spring
框架中专门用于拦截带有 @Transactional
注解方法的 AOP 拦截器
TransactionInterceptor
类继承自 TransactionAspectSupport
,并实现了 MethodInterceptor
接口。在 Spring
的事务自动代理机制中,@Transactional
注解会被 TransactionAttributeSource
解析,最终触发 TransactionInterceptor
的拦截逻辑
在 Spring 5.3.23
版本中,TransactionInterceptor
的核心逻辑如下:
/**
* AOP 方法拦截器的核心实现,用于在事务环境中执行目标方法
*/
@Override
@Nullable
public Object invoke(MethodInvocation invocation) throws Throwable {
// TransactionAttributeSource 需要同时传入目标类和方法(方法可能来自接口)
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// 委托给 TransactionAspectSupport 的核心事务处理方法。传入目标方法、目标类和自定义的调用回调
return invokeWithinTransaction(invocation.getMethod(), targetClass, new CoroutinesInvocationCallback() {
/**
* 继续执行拦截链,最终会调用目标方法
*/
@Override
@Nullable
public Object proceedWithInvocation() throws Throwable {
return invocation.proceed();
}
@Override
public Object getTarget() {
return invocation.getThis();
}
@Override
public Object[] getArguments() {
return invocation.getArguments();
}
});
}
invoke
方法:
AOP
拦截器的入口,负责拦截方法调用invokeWithinTransaction
方法:
整个调用链路可分为以下关键步骤:
// 关键调用链路伪代码
TransactionInterceptor.invoke()
→ TransactionAspectSupport.invokeWithinTransaction()
→ createTransactionIfNecessary() // 创建事务
→ AbstractPlatformTransactionManager.getTransaction()
→ DataSourceTransactionManager.doBegin() // 开启事务
→ invocation.proceedWithInvocation(); // 执行目标方法(包含MyBatis SQL)
→ commitTransactionAfterReturning() // 正常返回后提交
→ AbstractPlatformTransactionManager.commit()
→ DataSourceTransactionManager.doCommit()
→ completeTransactionAfterThrowing() // 异常时回滚
→ AbstractPlatformTransactionManager.rollback()
→ DataSourceTransactionManager.doRollback()
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
// 1. 获取或创建新的Connection
if (!txObject.hasConnectionHolder() ||
txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
Connection newCon = obtainDataSource().getConnection();
if (logger.isDebugEnabled()) {
logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
// 2. 准备Connection用于事务
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
con = txObject.getConnectionHolder().getConnection();
// 3. 设置隔离级别
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
txObject.setReadOnly(definition.isReadOnly());
// 4. 【关键】:关闭自动提交,开启事务
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
con.setAutoCommit(false);
}
// 5. 准备事务同步
prepareTransactionalConnection(con, definition);
txObject.getConnectionHolder().setTransactionActive(true);
// 6. 超时设置
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
// 7. 【关键】:将ConnectionHolder绑定到当前线程
if (txObject.isNewConnectionHolder()) {
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
}
}
catch (Throwable ex) {
// 异常处理...
}
}
关键步骤解析:
con.setAutoCommit(false)
开启事务模式TransactionSynchronizationManager.bindResource()
将 Connection
绑定到当前线程protected void doCommit(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
try {
// 核心:调用JDBC Connection的commit方法
con.commit();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not commit JDBC transaction", ex);
}
}
protected void doRollback(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
try {
// 核心:调用JDBC Connection的rollback方法
con.rollback();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
}
}
TransactionSynchronizationManager
是 Spring
事务管理的核心组件,使用ThreadLocal
存储当前线程的事务资源:
// org.springframework.transaction.support.TransactionSynchronizationManager (Spring 5.3.23)
private static final ThreadLocal<Map<Object, Object>> resources =
new NamedThreadLocal<>("Transactional resources");
private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations =
new NamedThreadLocal<>("Transaction synchronizations");
private static final ThreadLocal<String> currentTransactionName =
new NamedThreadLocal<>("Current transaction name");
// 绑定资源到当前线程
public static void bindResource(Object key, Object value) throws IllegalStateException {
Map<Object, Object> map = resources.get();
if (map == null) {
map = new HashMap<>();
resources.set(map);
}
Object oldValue = map.put(key, value);
if (oldValue != null) {
throw new IllegalStateException("Already value for key [" + key + "]");
}
}
// 从当前线程获取资源
public static Object getResource(Object key) {
Map<Object, Object> map = resources.get();
return (map != null ? map.get(key) : null);
}
关键绑定点:
在DataSourceTransactionManager.doBegin()
方法中,通过以下代码将 ConnectionHolder
绑定到当前线程:
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
SqlSessionTemplate
是 Spring
与 MyBatis
集成的核心组件,它会优先使用 Spring
管理的事务连接:
执行 SQL
的核心方法是通过动态代理实现的。具体来说,所有 SQL
操作都会被代理到SqlSessionInterceptor
类的invoke
方法中处理。这个方法会获取一个 SqlSession
实例,并调用其对应的 SQL
执行方法(如selectOne
、insert
、update
等)
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 1. 从Spring事务上下文中获取SqlSession(或创建新的)
SqlSession sqlSession = getSqlSession(
SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType,
SqlSessionTemplate.this.exceptionTranslator);
try {
// 2. 通过反射调用SqlSession的实际方法(如selectOne、insert等)
Object result = method.invoke(sqlSession, args);
// 3. 如果不是事务管理的SqlSession,则手动提交
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
// 异常处理...
} finally {
// 4. 关闭SqlSession(如果不是事务管理的)
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
getSqlSession()
方法最终会调用SqlSessionUtils
工具类,尝试从TransactionSynchronizationManager
获取当前事务上下文中的 SqlSession
:
/**
* 获取MyBatis的SqlSession实例,支持事务同步管理
*/
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
// 参数校验...
// 从当前事务同步管理器中获取已绑定的SqlSession资源
// 【核心逻辑】:事务中的SqlSession会绑定到当前线程
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
// 从现有持有者中获取SqlSession(优先使用已存在的会话)
SqlSession session = sessionHolder(executorType, holder);
if (session != null) {
return session;
}
// 调用MyBatis工厂方法创建新会话(指定执行器类型)
session = sessionFactory.openSession(executorType);
// 注册SqlSession到事务同步管理器(关键逻辑:实现事务内会话共享)
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
return session;
}
Spring
通过@Transactional
注解获取事务属性配置TransactionInterceptor
拦截目标方法调用DataSourceTransactionManager
实例doBegin()
:
Connection
autoCommit=false
Connection
绑定到TransactionSynchronizationManager
MyBatis
通过SqlSessionTemplate
获取 Spring
管理的 Connection
执行 SQL
doCommit()
或doRollback()
,最终调用 Connection
的对应方法Connection
,解除与当前线程的绑定理解 Spring
与 MyBatis
的事务协作机制,不仅能帮助我们正确使用事务,更能在遇到问题时快速定位和解决。完结撒花(*^▽^)!!!*