在 Spring Boot 中,MyBatis 的"自动提交"行为并不是 MyBatis 自己实现的,而是 由 Spring 框架通过事务管理器(PlatformTransactionManager)统一控制 的。MyBatis 原生默认是 不自动提交 (autoCommit=false),但在 Spring Boot 集成环境下,是否提交由 Spring 的事务传播机制决定:
一、核心结论
| 场景 | 是否自动提交? | 控制者 |
|---|---|---|
| 原生 MyBatis(无 Spring) | ❌ 默认不提交,需手动 sqlSession.commit() |
MyBatis 自身 |
Spring Boot + MyBatis(无 @Transactional) |
✅ 自动提交(每条 SQL 独立事务) | Spring 的 DataSourceTransactionManager |
Spring Boot + MyBatis(有 @Transactional) |
❌ 方法结束前不提交,成功则提交,异常则回滚 | Spring 事务管理 |
✅ 所以你看到的"自动提交",其实是 Spring 在方法无事务注解时,为每个数据库操作开启并立即提交一个独立事务。
二、Spring Boot 如何集成 MyBatis?
Spring Boot 通过 mybatis-spring-boot-starter 自动配置:
- 创建
SqlSessionFactory - 创建
SqlSessionTemplate(线程安全的 SqlSession 代理) - 注册 Mapper 接口为 Spring Bean
关键点:不再直接使用原生 SqlSession,而是通过 SqlSessionTemplate 代理调用。
三、源码分析:为什么"自动提交"?
1. SqlSessionTemplate 的代理机制
SqlSessionTemplate 是 MyBatis-Spring 提供的线程安全 SqlSession 实现,它内部持有一个 SqlSessionInterceptor(动态代理)。
当调用 Mapper 方法(如 userMapper.insert(user))时,实际执行路径:
Mapper Proxy → SqlSessionTemplate → SqlSessionInterceptor.invoke()
查看 SqlSessionInterceptor.invoke() 源码(简化版):
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. 执行 SQL(如 insert/update/delete/select)
Object result = method.invoke(sqlSession, args);
// 3. 如果当前没有 Spring 事务,则立即 commit(仅对更新操作)
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
sqlSession.commit(true); // true 表示强制提交
}
return result;
} catch (Throwable t) {
// 异常处理...
throw t;
} finally {
// 4. 如果不是 Spring 事务管理的 SqlSession,则关闭它
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
sqlSession.close();
}
}
}
🔍 关键判断:
isSqlSessionTransactional(...)
- 如果当前线程 已有 Spring 事务 (即处于
@Transactional方法中),则返回true,不提交、不关闭,交由 Spring 统一管理。- 如果 没有 Spring 事务 ,则返回
false,立即提交并关闭 SqlSession。
这就解释了"自动提交"的来源!
2. isSqlSessionTransactional 的实现逻辑
该方法检查当前 SqlSession 是否被 Spring 事务管理:
// org.mybatis.spring.SqlSessionUtils
public static boolean isSqlSessionTransactional(SqlSession session, SqlSessionFactory sessionFactory) {
// 从 ThreadLocal 中获取 Spring 管理的 SqlSession
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager
.getResource(sessionFactory);
return (holder != null) && (holder.getSqlSession() == session);
}
TransactionSynchronizationManager.getResource()是 Spring 事务同步的核心机制。- 只有在
@Transactional方法中,Spring 才会提前将SqlSession绑定到ThreadLocal。 - 否则,
holder == null,返回false→ 触发自动提交。
3. Spring 事务如何接管 SqlSession?
当方法标注 @Transactional 时,Spring 会:
- 在方法开始前,通过
DataSourceTransactionManager开启 JDBC 事务(connection.setAutoCommit(false))。 - 调用
SqlSessionUtils.getSqlSession()时,发现已有事务,复用同一个 Connection ,并将其包装为SqlSession存入ThreadLocal。 - 方法结束时,根据是否抛异常,调用
commit()或rollback()。 - 最终由 Spring 负责
close()。
此时,SqlSessionTemplate 不会干预提交行为。
四、验证实验
场景 1:无 @Transactional
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public void saveUser(User user) {
userMapper.insert(user); // 自动提交!
}
}
✅ 插入成功,即使后续代码抛异常,数据也不会回滚。
场景 2:有 @Transactional
@Transactional
public void saveUserWithTx(User user) {
userMapper.insert(user);
throw new RuntimeException("模拟异常"); // 数据会回滚!
}
❌ 插入被回滚,因为整个方法是一个事务。
五、配置项说明
虽然 MyBatis 有 autoCommit 配置,但在 Spring Boot 中 几乎无效,因为:
- Spring 通过
DataSource获取Connection,并由事务管理器控制setAutoCommit()。 - MyBatis 的
autoCommit参数在SqlSessionFactoryBean中会被忽略(或覆盖)。
所以,不要依赖 MyBatis 的 autoCommit,而应使用 Spring 的 @Transactional。
六、总结
| 问题 | 答案 |
|---|---|
| Spring Boot 中 MyBatis 为何"自动提交"? | 因为 Spring 在无事务方法中,为每次 Mapper 调用创建独立事务并立即提交。 |
| 源码关键类 | SqlSessionTemplate.SqlSessionInterceptor + SqlSessionUtils |
| 控制提交行为的方式 | 使用 @Transactional 注解,而非 MyBatis 配置 |
| 是否应关闭"自动提交"? | 不需要关闭,这是 Spring 的合理默认行为;需事务时加 @Transactional 即可 |
💡 最佳实践:
- 简单查询/单条写入:无需
@Transactional,享受自动提交。- 多操作一致性要求:必须加
@Transactional。- 永远不要在 Service 层手动调用
sqlSession.commit()!
通过 Spring 的声明式事务,开发者既能获得"自动提交"的便利,又能灵活控制复杂事务,这正是 Spring Boot + MyBatis 的强大之处。