mybatis的事务执行原理

57 阅读8分钟

MyBatis 的事务执行原理核心是  “委托式事务管理” —— 自身不直接实现事务的提交 / 回滚逻辑,而是依赖底层数据源(如 JDBC 事务)或整合 Spring 事务管理器,通过 Transaction 接口封装事务操作,确保 SQL 执行的原子性(要么全部成功,要么全部失败)。

整个机制可拆解为  “事务接口设计、事务管理模式、执行流程、与 Spring 整合”  四大核心部分,下面结合源码和实际使用场景详细说明:

一、核心前提:MyBatis 事务的依赖基础

MyBatis 事务的实现依赖 数据源(DataSource)  和 事务管理器(TransactionManager)

  1. 数据源(DataSource) :提供数据库连接(Connection),是事务的载体(JDBC 事务本质是基于 Connection 的 setAutoCommit()commit()rollback() 方法);
  2. 事务管理器(TransactionManager) :MyBatis 定义的事务管理接口,负责获取事务、提交 / 回滚事务,有两种实现(对应不同事务模式)。

关键结论:MyBatis 事务的底层本质是 JDBC 事务(默认)或 Spring 管理的事务(整合场景),MyBatis 仅提供统一的事务操作接口,不独立实现事务逻辑。

二、核心接口设计(MyBatis 事务的骨架)

MyBatis 通过 3 个核心接口封装事务操作,解耦事务管理与具体实现:

接口名称核心作用关键方法
Transaction封装单个事务的操作(提交 / 回滚 / 关闭)commit()(提交)、rollback()(回滚)、close()(关闭事务关联的连接)
TransactionManager事务管理器,负责创建 Transaction 实例getTransaction(TransactionIsolationLevel level)(获取事务)
TransactionFactory事务工厂,负责创建 TransactionManagernewTransactionManager(DataSource dataSource)(创建事务管理器)

1. Transaction 接口实现类(事务的具体载体)

MyBatis 提供 3 种 Transaction 实现,对应不同的连接管理方式:

  • JdbcTransaction(默认) :基于 JDBC 原生事务,直接使用 Connection 的事务方法(setAutoCommit(false) 关闭自动提交,执行后手动 commit()/rollback());
  • ManagedTransaction:“托管式事务”,不主动管理事务(不调用 commit()/rollback()),而是委托给外部容器(如 J2EE 容器)管理,适用于分布式事务场景;
  • SpringManagedTransaction:整合 Spring 事务时使用,委托 SpringTransactionManager 管理事务,与 Spring 事务同步(如 @Transactional 注解)。

2. TransactionManager 实现类(事务管理器的具体实现)

与 Transaction 一一对应,提供两种核心实现:

  • JdbcTransactionManager(默认) :创建 JdbcTransaction 实例,基于 JDBC 事务管理,适用于独立使用 MyBatis 的场景;
  • ManagedTransactionManager:创建 ManagedTransaction 实例,委托外部容器管理事务,适用于分布式事务或容器化部署场景。

三、MyBatis 事务的两种管理模式(独立使用 vs 整合 Spring)

MyBatis 的事务执行逻辑分两种场景:独立使用 MyBatis(自身管理事务)和 整合 Spring(Spring 管理事务),核心流程差异较大。

场景 1:独立使用 MyBatis(无 Spring,默认 JDBC 事务)

当不整合 Spring 时,MyBatis 通过 SqlSession 手动管理事务,核心依赖 JdbcTransactionManager 和 JdbcTransaction,流程如下:

核心原理
  • 事务与 SqlSession 绑定:一个 SqlSession 对应一个 Transaction(即一个 Connection),事务的提交 / 回滚通过 SqlSession 触发;
  • 关闭自动提交:事务启动时,MyBatis 会将 Connection.setAutoCommit(false),避免 SQL 执行后自动提交;
  • 手动控制提交 / 回滚:所有 SQL 执行完成后,手动调用 sqlSession.commit() 提交,或 sqlSession.rollback() 回滚;
  • 连接释放:sqlSession.close() 时,会关闭 Connection(或归还到连接池)。
代码示例(独立使用 MyBatis 事务)
// 1. 加载 MyBatis 配置文件(mybatis-config.xml)
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

// 2. 获取 SqlSession(默认不自动提交事务,autoCommit=false)
SqlSession sqlSession = sqlSessionFactory.openSession(); // 等价于 openSession(false)

try {
    // 3. 获取 Mapper 接口,执行 SQL
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    userMapper.insertUser(new User("张三"));
    userMapper.updateUserStatus(1, "ACTIVE");

    // 4. 所有 SQL 执行成功,提交事务
    sqlSession.commit();
} catch (Exception e) {
    // 5. 执行失败,回滚事务
    sqlSession.rollback();
    e.printStackTrace();
} finally {
    // 6. 关闭 SqlSession,释放连接
    sqlSession.close();
}
底层执行流程(对应代码示例)
  1. sqlSessionFactory.openSession()

    • 调用 JdbcTransactionManager.getTransaction(),获取 DataSource 的 Connection
    • 创建 JdbcTransaction 实例(绑定该 Connection),并调用 connection.setAutoCommit(false) 关闭自动提交;
    • 创建 SqlSession 实例,将 JdbcTransaction 绑定到 SqlSession 中。
  2. 执行 SQL(insertUser/updateUserStatus

    • SqlSession 从 JdbcTransaction 中获取 Connection,执行 SQL 操作;
    • 所有 SQL 都在同一个 Connection 中执行,共享同一个事务。
  3. sqlSession.commit()

    • 调用 JdbcTransaction.commit(),底层执行 connection.commit()
    • 提交后,Connection 可继续复用(若使用连接池)或关闭。
  4. sqlSession.rollback()

    • 调用 JdbcTransaction.rollback(),底层执行 connection.rollback()
    • 回滚后,恢复事务状态,释放连接。
  5. sqlSession.close()

    • 调用 JdbcTransaction.close(),关闭 Connection(或归还到连接池);
    • 销毁 SqlSession 与 Transaction 的绑定关系。
关键配置(mybatis-config.xml)

独立使用时,需配置数据源和事务管理器(默认无需显式配置,MyBatis 自动使用 JdbcTransactionManager):

xml

<configuration>
    <!-- 配置数据源(可使用连接池,如 Druid、C3P0) -->
    <environments default="development">
        <environment id="development">
            <!-- 事务管理器:默认 JdbcTransactionManager -->
            <transactionManager type="JDBC"/>
            <!-- 数据源:配置数据库连接信息 -->
            <dataSource type="POOLED"> <!-- POOLED 表示使用 MyBatis 内置连接池 -->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis_db"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

场景 2:整合 Spring(Spring 管理事务,主流场景)

实际开发中,MyBatis 几乎都会与 Spring 整合,此时事务由 Spring 事务管理器(PlatformTransactionManager  统一管理,MyBatis 仅负责执行 SQL,事务的提交 / 回滚由 Spring 控制。

核心原理
  • 事务管理委托:MyBatis 不再使用自身的 JdbcTransactionManager,而是通过 SpringManagedTransaction 委托 Spring 管理事务;
  • 连接同步:Spring 事务管理器会创建 Connection 并绑定到当前线程(ThreadLocal),MyBatis 执行 SQL 时从线程中获取该 Connection,确保所有 SQL 共享同一个事务;
  • 注解驱动:通过 @Transactional 注解声明事务(传播行为、隔离级别、超时时间等),Spring 自动拦截方法调用,触发事务的开启 / 提交 / 回滚;
  • 异常触发回滚:默认情况下,Spring 会在捕获到 未检查异常(RuntimeException 及其子类)  时触发回滚,检查异常(如 IOException)不会回滚(可通过 @Transactional(rollbackFor = Exception.class) 配置)。
核心组件依赖

整合需引入 3 个核心依赖(Maven 示例):

xml

<!-- MyBatis 核心依赖 -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.13</version>
</dependency>
<!-- MyBatis-Spring 整合依赖(关键,提供 SpringManagedTransaction 等) -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>2.0.13</version>
</dependency>
<!-- Spring 事务依赖 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>5.3.29</version>
</dependency>
核心配置(Spring 配置类)

需配置数据源、SqlSessionFactory、事务管理器,并开启事务注解支持:

@Configuration
@MapperScan("com.example.mapper") // 扫描 MyBatis Mapper 接口
@EnableTransactionManagement // 开启 Spring 事务注解支持
public class MyBatisSpringConfig {

    // 1. 配置数据源(使用 Druid 连接池为例)
    @Bean
    public DataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/mybatis_db");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        return dataSource;
    }

    // 2. 配置 SqlSessionFactory(整合 Spring 后,由 Spring 管理)
    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        // 配置 MyBatis 映射文件路径(可选)
        sessionFactory.setMapperLocations(
            new PathMatchingResourcePatternResolver().getResources("classpath:mapper/*.xml")
        );
        return sessionFactory.getObject();
    }

    // 3. 配置 Spring 事务管理器(核心!JDBC 事务管理器)
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        // 使用 Spring 的 DataSourceTransactionManager(基于 JDBC 事务)
        return new DataSourceTransactionManager(dataSource);
    }
}
代码示例(Spring 事务注解使用)
@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    // 声明式事务:该方法内的所有 SQL 原子执行
    @Transactional(
        propagation = Propagation.REQUIRED, // 事务传播行为(默认)
        isolation = Isolation.DEFAULT, // 事务隔离级别(默认,继承数据库级别)
        timeout = 30, // 超时时间(30 秒)
        rollbackFor = Exception.class // 所有异常都触发回滚
    )
    public void addUserAndUpdateStatus() {
        // 执行 SQL 操作(共享同一个事务)
        userMapper.insertUser(new User("李四"));
        userMapper.updateUserStatus(2, "ACTIVE");
        
        // 若抛出异常,Spring 会自动回滚事务
        // throw new RuntimeException("模拟异常,触发回滚");
    }
}
底层执行流程(整合 Spring 场景)
  1. 方法调用触发事务拦截

    • 当调用 addUserAndUpdateStatus() 时,Spring 的 TransactionInterceptor(事务拦截器)拦截方法;

    • 根据 @Transactional 注解配置,DataSourceTransactionManager 开启事务:

      • 获取 DataSource 的 Connection,调用 connection.setAutoCommit(false)
      • 将该 Connection 绑定到当前线程(ThreadLocal)。
  2. MyBatis 执行 SQL

    • SqlSession 通过 SpringManagedTransaction 从当前线程获取绑定的 Connection
    • 执行 insertUser 和 updateUserStatus 时,使用同一个 Connection,所有 SQL 都在当前事务中。
  3. 事务提交 / 回滚

    • 方法正常执行完成:DataSourceTransactionManager 调用 connection.commit() 提交事务;
    • 方法抛出异常(符合 rollbackFor 配置):调用 connection.rollback() 回滚事务。
  4. 连接释放

    • 事务完成后,SpringManagedTransaction 释放 Connection(归还到连接池),并移除线程绑定的 Connection

四、MyBatis 事务的关键特性与限制

1. 事务隔离级别

MyBatis 事务的隔离级别依赖底层数据库和事务管理器:

  • 独立使用时(JDBC 事务):通过 TransactionIsolationLevel 枚举指定(如 READ_COMMITTEDREPEATABLE_READ),底层调用 connection.setTransactionIsolation()
  • 整合 Spring 时:通过 @Transactional(isolation = Isolation.XXX) 配置,最终由 Spring 传递到底层数据库。

2. 事务传播行为

仅整合 Spring 时支持事务传播行为(MyBatis 独立使用时无传播行为概念),核心传播行为包括:

  • REQUIRED(默认):若当前存在事务,则加入事务;若不存在,则创建新事务;
  • REQUIRES_NEW:无论当前是否存在事务,都创建新事务(原事务暂停);
  • SUPPORTS:若当前存在事务,则加入事务;若不存在,则以非事务方式执行;
  • 其他:MANDATORYNOT_SUPPORTEDNEVERNESTED

3. 限制

  • 不支持分布式事务:MyBatis 自身仅支持单库事务,分布式事务需依赖 Spring Cloud Alibaba Seata、XA 协议等第三方方案;
  • 独立使用时功能有限:无传播行为、无注解支持,需手动控制 SqlSession 的提交 / 回滚,仅适用于简单场景;
  • 依赖数据库事务支持:MyBatis 事务本质是对 JDBC 事务的封装,数据库本身不支持的事务特性(如某些数据库的 SERIALIZABLE 隔离级别),MyBatis 也无法实现。

五、核心总结

  1. 底层本质:MyBatis 事务是对 JDBC 事务的封装(独立使用)或委托 Spring 事务管理(整合场景),自身不实现事务核心逻辑;
  2. 核心绑定:独立使用时,SqlSession ↔ Transaction ↔ Connection 一一绑定,事务通过 SqlSession 手动控制;
  3. 主流场景:整合 Spring 后,通过 @Transactional 注解实现声明式事务,Spring 负责事务的开启 / 提交 / 回滚,MyBatis 仅执行 SQL;
  4. 关键依赖:独立使用依赖 JdbcTransactionManager,整合 Spring 依赖 SpringManagedTransaction 和 DataSourceTransactionManager
  5. 使用建议:实际开发中优先选择 “Spring + MyBatis” 整合方案,利用 Spring 的声明式事务(@Transactional)简化事务管理,减少手动编码错误。

理解 MyBatis 事务的核心是抓住 “委托式管理”——MyBatis 负责 SQL 执行,事务的核心逻辑(提交 / 回滚、隔离级别、传播行为)依赖底层数据源或 Spring 事务管理器,避免重复造轮子。