MyBatis 的事务执行原理核心是 “委托式事务管理” —— 自身不直接实现事务的提交 / 回滚逻辑,而是依赖底层数据源(如 JDBC 事务)或整合 Spring 事务管理器,通过 Transaction 接口封装事务操作,确保 SQL 执行的原子性(要么全部成功,要么全部失败)。
整个机制可拆解为 “事务接口设计、事务管理模式、执行流程、与 Spring 整合” 四大核心部分,下面结合源码和实际使用场景详细说明:
一、核心前提:MyBatis 事务的依赖基础
MyBatis 事务的实现依赖 数据源(DataSource) 和 事务管理器(TransactionManager) :
- 数据源(DataSource) :提供数据库连接(
Connection),是事务的载体(JDBC 事务本质是基于Connection的setAutoCommit()、commit()、rollback()方法); - 事务管理器(TransactionManager) :MyBatis 定义的事务管理接口,负责获取事务、提交 / 回滚事务,有两种实现(对应不同事务模式)。
关键结论:MyBatis 事务的底层本质是 JDBC 事务(默认)或 Spring 管理的事务(整合场景),MyBatis 仅提供统一的事务操作接口,不独立实现事务逻辑。
二、核心接口设计(MyBatis 事务的骨架)
MyBatis 通过 3 个核心接口封装事务操作,解耦事务管理与具体实现:
| 接口名称 | 核心作用 | 关键方法 |
|---|---|---|
Transaction | 封装单个事务的操作(提交 / 回滚 / 关闭) | commit()(提交)、rollback()(回滚)、close()(关闭事务关联的连接) |
TransactionManager | 事务管理器,负责创建 Transaction 实例 | getTransaction(TransactionIsolationLevel level)(获取事务) |
TransactionFactory | 事务工厂,负责创建 TransactionManager | newTransactionManager(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();
}
底层执行流程(对应代码示例)
-
sqlSessionFactory.openSession():- 调用
JdbcTransactionManager.getTransaction(),获取DataSource的Connection; - 创建
JdbcTransaction实例(绑定该Connection),并调用connection.setAutoCommit(false)关闭自动提交; - 创建
SqlSession实例,将JdbcTransaction绑定到SqlSession中。
- 调用
-
执行 SQL(
insertUser/updateUserStatus) :SqlSession从JdbcTransaction中获取Connection,执行 SQL 操作;- 所有 SQL 都在同一个
Connection中执行,共享同一个事务。
-
sqlSession.commit():- 调用
JdbcTransaction.commit(),底层执行connection.commit(); - 提交后,
Connection可继续复用(若使用连接池)或关闭。
- 调用
-
sqlSession.rollback():- 调用
JdbcTransaction.rollback(),底层执行connection.rollback(); - 回滚后,恢复事务状态,释放连接。
- 调用
-
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 场景)
-
方法调用触发事务拦截:
-
当调用
addUserAndUpdateStatus()时,Spring 的TransactionInterceptor(事务拦截器)拦截方法; -
根据
@Transactional注解配置,DataSourceTransactionManager开启事务:- 获取
DataSource的Connection,调用connection.setAutoCommit(false); - 将该
Connection绑定到当前线程(ThreadLocal)。
- 获取
-
-
MyBatis 执行 SQL:
SqlSession通过SpringManagedTransaction从当前线程获取绑定的Connection;- 执行
insertUser和updateUserStatus时,使用同一个Connection,所有 SQL 都在当前事务中。
-
事务提交 / 回滚:
- 方法正常执行完成:
DataSourceTransactionManager调用connection.commit()提交事务; - 方法抛出异常(符合
rollbackFor配置):调用connection.rollback()回滚事务。
- 方法正常执行完成:
-
连接释放:
- 事务完成后,
SpringManagedTransaction释放Connection(归还到连接池),并移除线程绑定的Connection。
- 事务完成后,
四、MyBatis 事务的关键特性与限制
1. 事务隔离级别
MyBatis 事务的隔离级别依赖底层数据库和事务管理器:
- 独立使用时(JDBC 事务):通过
TransactionIsolationLevel枚举指定(如READ_COMMITTED、REPEATABLE_READ),底层调用connection.setTransactionIsolation(); - 整合 Spring 时:通过
@Transactional(isolation = Isolation.XXX)配置,最终由 Spring 传递到底层数据库。
2. 事务传播行为
仅整合 Spring 时支持事务传播行为(MyBatis 独立使用时无传播行为概念),核心传播行为包括:
REQUIRED(默认):若当前存在事务,则加入事务;若不存在,则创建新事务;REQUIRES_NEW:无论当前是否存在事务,都创建新事务(原事务暂停);SUPPORTS:若当前存在事务,则加入事务;若不存在,则以非事务方式执行;- 其他:
MANDATORY、NOT_SUPPORTED、NEVER、NESTED。
3. 限制
- 不支持分布式事务:MyBatis 自身仅支持单库事务,分布式事务需依赖 Spring Cloud Alibaba Seata、XA 协议等第三方方案;
- 独立使用时功能有限:无传播行为、无注解支持,需手动控制
SqlSession的提交 / 回滚,仅适用于简单场景; - 依赖数据库事务支持:MyBatis 事务本质是对 JDBC 事务的封装,数据库本身不支持的事务特性(如某些数据库的
SERIALIZABLE隔离级别),MyBatis 也无法实现。
五、核心总结
- 底层本质:MyBatis 事务是对 JDBC 事务的封装(独立使用)或委托 Spring 事务管理(整合场景),自身不实现事务核心逻辑;
- 核心绑定:独立使用时,
SqlSession↔Transaction↔Connection一一绑定,事务通过SqlSession手动控制; - 主流场景:整合 Spring 后,通过
@Transactional注解实现声明式事务,Spring 负责事务的开启 / 提交 / 回滚,MyBatis 仅执行 SQL; - 关键依赖:独立使用依赖
JdbcTransactionManager,整合 Spring 依赖SpringManagedTransaction和DataSourceTransactionManager; - 使用建议:实际开发中优先选择 “Spring + MyBatis” 整合方案,利用 Spring 的声明式事务(
@Transactional)简化事务管理,减少手动编码错误。
理解 MyBatis 事务的核心是抓住 “委托式管理”——MyBatis 负责 SQL 执行,事务的核心逻辑(提交 / 回滚、隔离级别、传播行为)依赖底层数据源或 Spring 事务管理器,避免重复造轮子。