★ Spring 事务与 MyBatis 如何集成?SqlSession 如何与事务同步?

结论先行:mybatis-spring 通过 SqlSessionTemplate 让 MyBatis 接入 Spring 事务。要点:DataSource 交给 Spring 管(DataSourceTransactionManager);事务开启时 SqlSession 与事务绑定复用,事务结束(提交/回滚)才关闭会话——这正是 MyBatis 一级缓存“跨 Mapper 方法共享”能成立的前提,也解释了为什么必须把 @Transactional 放在 Service 边界而非 Mapper 上。

一、集成组件

组件职责
SqlSessionFactoryBean用 Spring 的 DataSource 构建 SqlSessionFactory
SqlSessionTemplate线程安全的 SqlSession 代理,自动参与事务
MapperScannerConfigurer / @MapperScan扫描 Mapper 接口生成代理 Bean
DataSourceTransactionManagerSpring 统一管理数据库事务

mybatis-spring-boot-starter 会自动化前三者:默认装配 SqlSessionFactory、SqlSessionTemplate,并用启动类上的 @MapperScan 注册 Mapper。

二、SqlSession 与事务的同步机制

@Service
public class OrderService {
    @Transactional // REQUIRED:有事务则加入,无则新建
    public void createOrder(Order order) {
        userMapper.insert(order.getUserId());   // 同一 SqlSession
        orderMapper.insert(order);              // 同一连接/同一事务
    }
}

同步链路:

  1. 进入 @Transactional → 事务管理器开启事务,绑定 DataSource 连接;
  2. Mapper 方法调用 SqlSessionTemplate 时,检查到事务同步已激活 → 从 TransactionSynchronizationManager 取出绑定当前事务的 SqlSession 复用;
  3. 方法内多次 Mapper 调用共用同一 SqlSession 与连接,一级缓存因此生效;
  4. 方法正常结束 → 统一 commit;抛 RuntimeException → 统一 rollback;
  5. 事务结束后 SqlSession 才被关闭/归还,避免连接泄漏。

三、关键坑与结论

  • 事务必须跨在 Service 方法上(含多 Mapper 调用),放单条 Mapper 上无意义;
  • @Transactional 默认只回滚 RuntimeException 与 Error,受检异常需 rollbackFor=Exception.class;
  • 同一类内方法自调用(this.method())不经过代理 → 事务失效;
  • 无事务时 SqlSessionTemplate 每次调用创建并关闭独立 SqlSession,一级缓存不跨调用。

常见追问 / 记忆点

  • 追问:SqlSessionTemplate 和 SqlSession 什么关系?答:前者是线程安全门面,内部按事务状态分发/复用真正的 SqlSession。
  • 追问:为什么要在 Service 层开事务?答:跨 Mapper 的原子性由方法边界决定,事务应与业务用例对齐而非与单条 SQL 对齐。
  • 记忆点:模板代理 + 事务同步复用会话;事务边界=Service 方法,回滚看异常类型,自调用必失效。
笔记加载中…