java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > MyBatis事务管理

MyBatis事务管理模块详解

作者:探索java

本文主要介绍了MyBatis事务管理模块详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

1 引言

在企业级应用开发中,事务管理是保证数据一致性与完整性的核心机制。无论是银行转账、订单支付,还是库存扣减,这些操作都需要保证要么全部成功,要么全部失败,否则就会产生数据不一致的严重问题。

在 Java 生态中,MyBatis 作为一款轻量级 ORM 框架,因其灵活的 SQL 定制能力和较低的学习成本被广泛应用。然而,MyBatis 本身并不是一个“全栈”持久层框架,它并不直接管理数据源的生命周期,而是通过事务管理机制协调 JDBC 连接的提交与回滚。

理解 MyBatis 的事务管理机制不仅有助于我们编写正确、健壮的代码,还能帮助我们在与 Spring 等框架整合时,精准定位事务相关问题(如事务未生效、连接泄漏、跨数据源事务失败等)。

本系列文章的目标是从概念、实现、源码、整合、优化五个维度,深入剖析 MyBatis 的事务管理机制,并给出实战中的最佳实践。

1.1 为什么事务管理如此重要

数据库事务(Database Transaction)是保证ACID 特性(原子性 Atomicity、一致性 Consistency、隔离性 Isolation、持久性 Durability)的基础。在分布式和高并发系统中,事务问题会被放大:

MyBatis 的事务管理器相当于一个交通指挥员,它决定何时让 SQL 执行、何时提交或回滚,保证数据的一致性与安全性。

1.2 本文的核心关注点

本博客将重点分析以下几个方面:

2 核心概念

在深入源码分析之前,我们需要先厘清 MyBatis 事务管理的几个核心概念。这不仅包括数据库层面的事务定义,还涉及 MyBatis 在架构上对事务的抽象与实现。

2.1 事务的定义与特性

事务(Transaction)是数据库操作的一个逻辑单元,由一组 SQL 语句组成。这些语句要么全部成功提交,要么全部回滚撤销,确保数据的一致性。事务必须满足 ACID 四大特性:

2.2 MyBatis 中的事务抽象

MyBatis 将事务管理功能抽象为一个核心接口和若干实现类,主要包括:

public interface Transaction {
    Connection getConnection() throws SQLException;
    void commit() throws SQLException;
    void rollback() throws SQLException;
    void close() throws SQLException;
    Integer getTimeout() throws SQLException;
}

上面的 Transaction 接口就是 MyBatis 的事务核心抽象,所有事务管理器都必须实现该接口,以统一事务操作方式。

2.3 MyBatis 中的事务边界

在 MyBatis 中,事务边界主要由 SqlSession 控制:

如果与 Spring 整合,事务边界的控制权则由 Spring 的 PlatformTransactionManager 及其实现类(如 DataSourceTransactionManager)接管。

2.4 概念小结

从架构设计来看,MyBatis 的事务管理分为三个层次:

3 MyBatis 事务管理机制

MyBatis 提供了两种核心事务管理机制:JDBC 事务MANAGED 事务。它们的区别主要在于事务控制权的归属以及事务生命周期的管理方式。

3.1 JDBC 事务

JDBC 事务 是 MyBatis 默认的事务管理方式,由 MyBatis 直接通过 JDBC API 控制事务的提交与回滚。

工作机制

示例代码:

Transaction tx = new JdbcTransaction(dataSource, level, false);
try (Connection conn = tx.getConnection()) {
    // 执行 SQL
    tx.commit();
} catch (SQLException e) {
    tx.rollback();
} finally {
    tx.close();
}

优点:简单直接。 缺点:无法与外部事务资源(如 JTA)协作。

3.2 MANAGED 事务

MANAGED 事务 不直接控制事务提交与回滚,交由外部容器(如 Spring、JEE 容器)管理。

工作机制

配置示例:

<transactionManager type="MANAGED">
    <property name="closeConnection" value="false"/>
</transactionManager>

3.3 配置对比

<!-- JDBC 事务配置 -->
<transactionManager type="JDBC"/>
 
<!-- MANAGED 事务配置 -->
<transactionManager type="MANAGED">
    <property name="closeConnection" value="false"/>
</transactionManager>

3.4 差异总结

对比项JDBC 事务MANAGED 事务
控制权MyBatis 内部外部容器
提交/回滚MyBatis 调用 commit()/rollback()外部容器负责
连接关闭MyBatis 内部可由外部容器
场景独立 MyBatis 项目Spring、JEE 容器环境

4 MyBatis 与 Spring 的事务整合

在实际开发中,MyBatis 很少单独使用,更多是在 Spring 或 Spring Boot 框架中与其他数据访问技术(如 JPA、JDBC Template)共存。为了在多种数据访问方式间实现统一的事务控制,Spring 接管了 MyBatis 的事务管理。

4.1 整合的核心思想

MyBatis 原本通过 Transaction 接口及其实现类(如 JdbcTransaction、ManagedTransaction)来管理事务。在与 Spring 整合后,事务的开启、提交、回滚等生命周期由 Spring 的 PlatformTransactionManager(通常是 DataSourceTransactionManager)全权负责。

4.2 核心组件

4.3 事务接管流程

流程图示意:

@Transactional 方法调用
       ↓
TransactionInterceptor 拦截
       ↓
PlatformTransactionManager 启动事务
       ↓
绑定连接到 TransactionSynchronizationManager
       ↓
MyBatis 执行 SQL(SpringManagedTransaction 获取连接)
       ↓
方法结束,统一提交或回滚事务

4.4 配置示例(Spring Boot)

@Configuration
@MapperScan("com.example.mapper")
public class MyBatisConfig {
 
    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        factoryBean.setTransactionFactory(new SpringManagedTransactionFactory());
        return factoryBean.getObject();
    }
}

4.5 关键点总结

5 MyBatis 事务管理源码解析

本节从源码角度系统拆解 MyBatis 事务管理的关键接口与实现,并补充与 Spring 整合时的调用链路。示例以 MyBatis 3.x 与 mybatis-spring 2.x 为蓝本,代码片段做了少量删减与注释以便理解。

5.1 总览:类关系与模块边界

从宏观上看,事务相关模块可以分为四层:

类图(简化版):

classDiagram
  interface Transaction {
    +getConnection() Connection
    +commit()
    +rollback()
    +close()
    +getTimeout() Integer
  }
  class TransactionFactory {
    <<interface>>
    +newTransaction(Connection)
    +newTransaction(DataSource, level, autoCommit)
  }
  class JdbcTransaction
  class ManagedTransaction
  class JdbcTransactionFactory
  class ManagedTransactionFactory
  class SpringManagedTransaction
 
  Transaction <|.. JdbcTransaction
  Transaction <|.. ManagedTransaction
  Transaction <|.. SpringManagedTransaction
  TransactionFactory <|.. JdbcTransactionFactory
  TransactionFactory <|.. ManagedTransactionFactory

5.2Transaction接口逐行解读

public interface Transaction {
    Connection getConnection() throws SQLException;  // 懒加载或直接返回当前连接
    void commit() throws SQLException;               // 提交当前连接的事务
    void rollback() throws SQLException;             // 回滚当前连接的事务
    void close() throws SQLException;                // 归还连接或实际关闭
    Integer getTimeout() throws SQLException;        // 可选的事务超时
}

设计要点:

5.3JdbcTransaction源码要点

JdbcTransaction 直接基于 DataSourceConnection 控制事务:

public class JdbcTransaction implements Transaction {
  private final DataSource dataSource;
  private Connection connection;
  private final TransactionIsolationLevel level;
  private final boolean autoCommit;
 
  @Override
  public Connection getConnection() throws SQLException {
    if (connection == null) {
      connection = dataSource.getConnection();
      if (level != null) connection.setTransactionIsolation(level.getLevel());
      setDesiredAutoCommit(autoCommit);
    }
    return connection;
  }
 
  private void setDesiredAutoCommit(boolean desired) throws SQLException {
    if (connection.getAutoCommit() != desired) {
      connection.setAutoCommit(desired); // JDBC 事务关键:autoCommit=false 以启用显式提交
    }
  }
 
  @Override
  public void commit() throws SQLException {
    if (connection != null && !connection.getAutoCommit()) connection.commit();
  }
 
  @Override
  public void rollback() throws SQLException {
    if (connection != null && !connection.getAutoCommit()) connection.rollback();
  }
 
  @Override
  public void close() throws SQLException {
    if (connection != null) {
      resetAutoCommit(); // 归还连接前恢复状态(对连接池很重要)
      connection.close();
    }
  }
}

关键点:

示例:独立 MyBatis(非 Spring)使用

SqlSessionFactory factory = ...;
try (SqlSession session = factory.openSession(false)) { // 关闭自动提交
    UserMapper mapper = session.getMapper(UserMapper.class);
    mapper.insert(u1);
    mapper.insert(u2);
    session.commit();
} catch (Exception ex) {
    session.rollback();
}

5.4ManagedTransaction源码要点

ManagedTransaction 交由外部容器管理提交与回滚:

public class ManagedTransaction implements Transaction {
  private final DataSource dataSource;
  private Connection connection;
  private final boolean closeConnection; // 是否在 close() 时关闭连接
 
  @Override
  public Connection getConnection() throws SQLException {
    if (connection == null) {
      connection = dataSource.getConnection(); // 来自容器/代理
    }
    return connection;
  }
 
  @Override public void commit() {}     // 空实现,由外部容器负责
  @Override public void rollback() {}
  @Override public void close() throws SQLException {
    if (closeConnection && connection != null) connection.close();
  }
}

关键点:

5.5TransactionFactory与具体工厂

TransactionFactory 把选择权在配置期就确定下来:

public interface TransactionFactory {
  default void setProperties(Properties props) {}
  Transaction newTransaction(Connection conn);
  Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit);
}

两个常用实现:

XML 配置示例:

<environments default="dev">
  <environment id="dev">
    <transactionManager type="JDBC"/>
    <dataSource type="POOLED">...</dataSource>
  </environment>
</environments>

5.6 执行器如何驱动事务:BaseExecutor

MyBatis 的 Executor 负责发起 SQL 执行,并在恰当时机触发事务提交/回滚/关闭:

public abstract class BaseExecutor implements Executor {
  protected final Transaction transaction;
 
  @Override
  public void commit(boolean required) throws SQLException {
    if (closed) throw new ExecutorException("Closed");
    clearLocalCache();
    if (required) {
      transaction.commit(); // 委托给 Transaction 实现
    }
  }
 
  @Override
  public void rollback(boolean required) throws SQLException {
    if (!closed) {
      clearLocalCache();
      if (required) {
        transaction.rollback();
      }
    }
  }
 
  @Override
  public void close(boolean forceRollback) {
    try {
      try {
        if (forceRollback) rollback(true);
      } finally {
        transaction.close();
      }
    } catch (SQLException e) { ... }
  }
}

要点:

5.7 与 Spring 整合的源码链路

当引入 mybatis-spring 后,调用链整合如下:

关键类片段:

// mybatis-spring: SpringManagedTransaction#getConnection
public Connection getConnection() throws SQLException {
  if (this.connection == null) {
    this.connection = DataSourceUtils.getConnection(this.dataSource);
  }
  return this.connection;
}
 
// Spring: DataSourceUtils.getConnection
public static Connection getConnection(DataSource ds) {
  ConnectionHolder holder = (ConnectionHolder)
      TransactionSynchronizationManager.getResource(ds);
  if (holder != null && holder.hasConnection()) {
    return holder.getConnection();
  }
  Connection con = ds.getConnection();
  // 如果处于事务中,包装为 ConnectionHolder 并绑定到线程
  // ...
  return con;
}

SqlSession 获取与释放SqlSessionUtils):

public static SqlSession getSqlSession(SqlSessionFactory factory, ExecutorType type, PersistenceExceptionTranslator translator) {
  SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(factory);
  if (holder != null) {
    return holder.getSqlSession(); // 复用当前事务内的 SqlSession
  }
  SqlSession session = factory.openSession(type);
  // 如果存在事务,则注册同步器,在事务完成时关闭 SqlSession
  // TransactionSynchronizationManager.registerSynchronization(...)
  return session;
}

5.8 时序图:@Transactional 方法的一次完整调用

sequenceDiagram
  participant C as Client
  participant S as @Transactional Service
  participant TI as TransactionInterceptor
  participant TM as DataSourceTransactionManager
  participant TSM as TransactionSynchronizationManager
  participant SUT as SqlSessionTemplate
  participant SMT as SpringManagedTransaction
  participant DS as DataSource/Pool
  participant DB as Database
 
  C->>S: 调用业务方法
  S->>TI: AOP 拦截
  TI->>TM: begin() 开启事务
  TM->>DS: getConnection()
  DS-->>TM: Connection
  TM->>TSM: 绑定 ConnectionHolder
  TI->>S: 继续执行业务逻辑
  S->>SUT: 执行 Mapper 方法
  SUT->>SMT: getConnection()
  SMT->>TSM: 查询线程绑定 Connection
  TSM-->>SMT: 返回同一 Connection
  SMT->>DB: 执行 SQL
  SUT-->>S: 返回结果
  S->>TI: 方法结束
  TI->>TM: commit()/rollback()
  TM->>TSM: 清理并释放连接
  TM->>DS: 归还连接到连接池

5.9 边界条件与细节

5.10 调试与定位建议

打开日志

# MyBatis 执行与 JDBC 交互
logging.level.org.apache.ibatis=DEBUG
logging.level.jdbc.sqlonly=DEBUG
 
# Spring 事务与同步
logging.level.org.springframework.jdbc.datasource=DEBUG
logging.level.org.springframework.transaction=DEBUG
logging.level.org.mybatis.spring=DEBUG

打印连接标识:通过拦截器或日志模式打印 Connection#hashCode(),确认同一事务内是否为同一连接。

异常边界:务必在 @Transactional 入口层抛出(或显式标注)需要回滚的异常类型,否则可能出现“方法抛异常但未回滚”的错觉。

6 MyBatis 事务管理常见问题与解决方案

在实际项目中,MyBatis 的事务管理并非总是“开箱即用”,很多问题往往与配置、整合方式、事务传播等相关。下面我们列出常见问题、成因以及对应解决方案。

6.1 事务未生效

问题现象@Transactional 注解标记的方法中执行多条 SQL,但数据库最终只部分提交,或回滚未生效。

常见原因

解决方案

6.2 数据库连接泄漏

问题现象:系统长时间运行后,连接池中的连接耗尽,出现 java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available

常见原因

解决方案

6.3 多数据源事务问题

问题现象:跨多个数据源的操作无法在同一事务中提交或回滚。

常见原因

解决方案

6.4 嵌套事务与传播行为异常

问题现象:方法调用链中,内层事务的回滚影响了外层事务,或者预期的独立事务没有生效。

常见原因

解决方案

6.5 MyBatis 缓存与事务提交

问题现象:事务提交后,查询结果仍是旧数据。

常见原因

解决方案

到此这篇关于MyBatis事务管理模块详解的文章就介绍到这了,更多相关MyBatis事务管理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文