MyBatis事务管理模块详解

全面解析MyBatis事务管理机制,助你掌握数据一致性的核心技术

一、MyBatis整体架构与事务管理模块

在深入事务管理模块之前,我们先了解MyBatis的整体架构,以及事务管理模块在其中的重要地位。

从上图可以看出,MyBatis采用了分层架构设计,而事务管理模块(Transaction)位于基础支撑层,是保证数据一致性的关键组件。

1.1 事务管理模块的核心职责

事务管理模块主要承担以下核心职责:

复制代码
✅ 管理数据库事务 - 控制事务的开始、提交和回滚
✅ 事务提交和回滚 - 根据操作结果决定提交或回滚事务
✅ 连接管理 - 获取和释放数据库连接
✅ 与不同框架集成 - 支持与Spring、Guice等框架的集成
✅ 隔离级别控制 - 支持不同的事务隔离级别

1.2 什么是事务

事务(Transaction) 是数据库操作的基本单元,包含一系列操作,这些操作要么全部成功,要么全部失败

事务具有四个重要特性(ACID):

特性 说明 示例场景
原子性Atomicity 事务中的操作要么全部成功要么全部失败 转账:扣款和存款同时成功或同时失败
一致性Consistency 事务执行前后数据库状态保持一致 转账前后总金额不变
隔离性Isolation 并发事务之间相互隔离 一个事务看不到其他未提交事务的数据
持久性Durability 事务提交后数据永久保存 即使系统崩溃提交的数据也不会丢失

二、Transaction接口架构

MyBatis的事务管理模块采用了简洁的接口设计。

2.1 Transaction接口

Transaction是事务管理的顶层接口:

csharp 复制代码
public interface Transaction {
    // 获取数据库连接
    Connection getConnection() throws SQLException;

    // 提交事务
    void commit() throws SQLException;

    // 回滚事务
    void rollback() throws SQLException;

    // 关闭连接
    void close() throws SQLException;

    // 获取事务超时时间
    Integer getTimeout() throws SQLException;
}

2.2 Transaction实现类

MyBatis提供了多种Transaction实现:

scss 复制代码
Transaction (接口)
  ├── JdbcTransaction (JDBC事务)
  ├── ManagedTransaction (托管事务)
  └── 自定义Transaction实现

JdbcTransaction是使用JDBC原生方式管理事务的实现:

java 复制代码
public class JdbcTransaction implements Transaction {
    protected Connection connection;
    protected DataSource dataSource;
    protected TransactionIsolationLevel level;
    protected boolean autoCommit;

    public JdbcTransaction(DataSource ds, 
                          TransactionIsolationLevel desiredLevel, 
                          boolean desiredAutoCommit) {
        dataSource = ds;
        level = desiredLevel;
        autoCommit = desiredAutoCommit;
    }

    @Override
    public Connection getConnection() throws SQLException {
        if (connection == null) {
            openConnection();
        }
        return connection;
    }

    @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) {
            connection.close();
            connection = null;
        }
    }

    private void openConnection() throws SQLException {
        connection = dataSource.getConnection();
        if (level != null) {
            connection.setTransactionIsolation(level.getLevel());
        }
        if (autoCommit != null 
            && autoCommit != connection.getAutoCommit()) {
            connection.setAutoCommit(autoCommit);
        }
    }
}

ManagedTransaction让容器来管理事务的生命周期:

java 复制代码
public class ManagedTransaction implements Transaction {
    private DataSource dataSource;
    private TransactionIsolationLevel level;
    private boolean closeConnection;

    public ManagedTransaction(DataSource ds, 
                             TransactionIsolationLevel level, 
                             boolean closeConnection) {
        this.dataSource = ds;
        this.level = level;
        this.closeConnection = closeConnection;
    }

    @Override
    public Connection getConnection() throws SQLException {
        if (connection == null) {
            openConnection();
        }
        return connection;
    }

    @Override
    public void commit() throws SQLException {
        // 不做任何事,由容器管理提交
    }

    @Override
    public void rollback() throws SQLException {
        // 不做任何事,由容器管理回滚
    }

    @Override
    public void close() throws SQLException {
        if (closeConnection && connection != null) {
            connection.close();
            connection = null;
        }
    }
}

三、事务管理流程

完整的事务管理流程是确保数据一致性的关键。

3.1 事务生命周期

一个完整的事务生命周期包括以下阶段:

ini 复制代码
// 1. 开启事务
SqlSession session = sqlSessionFactory.openSession();

try {
    // 2. 执行数据库操作
    User user = new User("张三", "zhangsan@example.com");
    userMapper.insert(user);

    Order order = new Order(user.getId(), "iPhone 15", 6999);
    orderMapper.insert(order);

    // 3. 提交事务
    session.commit();
} catch (Exception e) {
    // 4. 回滚事务
    session.rollback();
    throw e;
} finally {
    // 5. 关闭会话
    session.close();
}

3.2 自动提交与手动提交

MyBatis支持两种事务提交模式:

ini 复制代码
// 自动提交模式:每条SQL自动提交
SqlSession session = sqlSessionFactory.openSession(true);
try {
    User user = new User("张三", "zhangsan@example.com");
    userMapper.insert(user);
    // 无需手动提交,自动提交
} finally {
    session.close();
}
// 手动提交模式:需要显式提交
SqlSession session = sqlSessionFactory.openSession(false);
try {
    User user = new User("张三", "zhangsan@example.com");
    userMapper.insert(user);

    Order order = new Order(user.getId(), "iPhone 15", 6999);
    orderMapper.insert(order);

    // 多条SQL在同一事务中
    session.commit(); // 显式提交
} catch (Exception e) {
    session.rollback(); // 显式回滚
    throw e;
} finally {
    session.close();
}

3.3 Executor中的事务管理

Executor在执行SQL时会涉及到事务管理:

scss 复制代码
public abstract class BaseExecutor implements Executor {
    private Transaction transaction;

    @Override
    public int update(MappedStatement ms, Object parameter) 
            throws SQLException {
        ErrorContext.instance().begin(ms.getId(), 
            ms.getSqlSource().getBoundSql(parameter));
        try {
            // 清空一级缓存
            clearLocalCache();

            // 执行更新操作
            return doUpdate(ms, parameter);
        } catch (SQLException e) {
            throw ExceptionFactory.wrapException(
                "Error updating database. Cause: " + e, e);
        } finally {
            ErrorContext.instance().end();
        }
    }

    @Override
    public void commit(boolean required) throws SQLException {
        if (closed) {
            throw new ExecutorException(
                "Cannot commit, transaction is already closed");
        }

        // 清空一级缓存
        clearLocalCache();

        // 刷新批处理
        flushStatements();

        // 提交事务
        if (required) {
            transaction.commit();
        }
    }

    @Override
    public void rollback(boolean required) throws SQLException {
        if (closed) {
            throw new ExecutorException(
                "Cannot rollback, transaction is already closed");
        }

        // 清空一级缓存
        clearLocalCache();

        // 刷新批处理
        flushStatements(true);

        // 回滚事务
        if (required) {
            transaction.rollback();
        }
    }
}

四、事务提交和回滚机制

事务的提交和回滚是保证数据一致性的核心机制。

4.1 事务提交流程

java 复制代码
// SqlSession中的提交实现
@Override
public void commit() {
    boolean commit = required;
    try {
        // 执行所有挂起的SQL语句
        flushStatements();

        if (commit) {
            // 委托给Executor提交
            executor.commit(isCommitOrRollbackRequired(false));
        }
    } catch (Exception e) {
        throw ExceptionFactory.wrapException(
            "Error committing transaction. Cause: " + e, e);
    }
}

// Executor中的提交实现
@Override
public void commit(boolean required) throws SQLException {
    if (closed) {
        throw new ExecutorException(
            "Cannot commit, transaction is already closed");
    }

    if (required) {
        // 清空一级缓存
        clearLocalCache();

        // 刷新批处理
        flushStatements();

        // 提交数据库事务
        transaction.commit();
    }
}

4.2 事务回滚流程

java 复制代码
// SqlSession中的回滚实现
@Override
public void rollback() {
    try {
        // 执行所有挂起的SQL语句
        flushStatements(true);

        // 委托给Executor回滚
        executor.rollback(isCommitOrRollbackRequired(true));
    } catch (Exception e) {
        throw ExceptionFactory.wrapException(
            "Error rolling back transaction. Cause: " + e, e);
    }
}

// Executor中的回滚实现
@Override
public void rollback(boolean required) throws SQLException {
    if (closed) {
        throw new ExecutorException(
            "Cannot rollback, transaction is already closed");
    }

    if (required) {
        // 清空一级缓存
        clearLocalCache();

        // 刷新批处理
        flushStatements(true);

        // 回滚数据库事务
        transaction.rollback();
    }
}

4.3 回滚触发条件

以下情况会触发事务回滚:

ini 复制代码
try {
    userMapper.insert(user);
    orderMapper.insert(order);
    session.commit();
} catch (Exception e) {
    session.rollback();
    throw e;
}
if (!isValid(order)) {
    session.rollback();
    return;
}
session.commit();
SqlSession session = sqlSessionFactory.openSession(
    ExecutorType.BATCH);
try {
    List<User> users = getBatchUsers();
    for (User user : users) {
        userMapper.insert(user);
    }
    session.commit();
} catch (Exception e) {
    session.rollback();
    throw e;
} finally {
    session.close();
}

4.4 事务隔离级别

MyBatis支持标准的JDBC事务隔离级别:

scss 复制代码
public enum IsolationLevel {
    NONE(Connection.TRANSACTION_NONE),
    READ_COMMITTED(Connection.TRANSACTION_READ_COMMITTED),
    READ_UNCOMMITTED(Connection.TRANSACTION_READ_UNCOMMITTED),
    REPEATABLE_READ(Connection.TRANSACTION_REPEATABLE_READ),
    SERIALIZABLE(Connection.TRANSACTION_SERIALIZABLE);

    public int getLevel() {
        return level;
    }
}

配置方式:

xml 复制代码
<!-- 在mybatis-config.xml中配置 -->
<settings>
    <setting name="defaultTransactionIsolationLevel" 
             value="READ_COMMITTED"/>
</settings>

<!-- 在DataSource中配置 -->
<dataSource type="POOLED">
    <property name="driver" value="${driver}"/>
    <property name="url" value="${url}"/>
    <property name="username" value="${username}"/>
    <property name="password" value="${password}"/>
    <!-- 设置默认隔离级别 -->
    <property name="defaultTransactionIsolationLevel" 
              value="2"/> <!-- READ_COMMITTED -->
</dataSource>

五、与Spring框架集成

在实际项目中,MyBatis通常与Spring集成,由Spring统一管理事务。

5.1 Spring事务配置

xml 复制代码
<!-- 配置事务管理器 -->
<bean id="transactionManager" 
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

<!-- 配置事务通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="select*" read-only="true"/>
        <tx:method name="get*" read-only="true"/>
        <tx:method name="query*" read-only="true"/>
        <tx:method name="*" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>

<!-- 配置事务切面 -->
<aop:config>
    <aop:pointcut id="servicePointcut" 
                  expression="execution(* com.example.service.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" 
                 pointcut-ref="servicePointcut"/>
</aop:config>
@Configuration
@EnableTransactionManagement
public class MyBatisConfig {

    @Bean
    public DataSourceTransactionManager transactionManager(
            DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(
            DataSource dataSource) throws Exception {
        SqlSessionFactoryBean sessionFactory = 
            new SqlSessionFactoryBean();
        sessionFactory.setDataSource(dataSource);
        sessionFactory.setMapperLocations(
            new PathMatchingResourcePatternResolver()
                .getResources("classpath:mapper/*.xml"));
        return sessionFactory;
    }

    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer() {
        MapperScannerConfigurer scanner = 
            new MapperScannerConfigurer();
        scanner.setBasePackage("com.example.mapper");
        return scanner;
    }
}

5.2 使用@Transactional注解

在Service层使用@Transactional注解:

typescript 复制代码
@Service
public class UserService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private OrderMapper orderMapper;

    // 默认事务管理
    @Transactional
    public void createUserWithOrder(User user, Order order) {
        userMapper.insert(user);
        orderMapper.insert(order);
    }

    // 只读事务
    @Transactional(readOnly = true)
    public User getUserById(Long id) {
        return userMapper.selectById(id);
    }

    // 指定隔离级别
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void updateUser(User user) {
        userMapper.update(user);
    }

    // 指定传播行为
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void logOperation(String operation) {
        // 在新事务中执行
    }

    // 指定回滚异常
    @Transactional(rollbackFor = {Exception.class})
    public void transfer(Long fromId, Long toId, BigDecimal amount) {
        userMapper.decrease(fromId, amount);
        userMapper.increase(toId, amount);
    }

    // 不回滚特定异常
    @Transactional(noRollbackFor = {BusinessException.class})
    public void processWithBusinessException() {
        // ...
    }

    // 超时设置
    @Transactional(timeout = 30)
    public void longRunningOperation() {
        // ...
    }
}

5.3 Spring事务传播行为

Spring支持多种事务传播行为:

传播行为 说明 使用场景
REQUIRED(默认) 如果当前存在事务,则加入否则创建新事务 大多数情况
REQUIRES_NEW 总是创建新事务挂起当前事务 日志记录审计
SUPPORTS 如果当前存在事务,则加入否则非事务执行 查询操作
NOT_SUPPORTED 总是非事务执行挂起当前事务 批量导入
NEVER 总是非事务执行如果存在事务则抛异常 不需要事务的操作
MANDATORY 必须在事务中执行否则抛异常 必须有事务的操作
NESTED 如果当前存在事务则嵌套执行 嵌套业务逻辑

5.4 集成示例

java 复制代码
@Service
public class OrderService {

    @Autowired
    private UserService userService;

    @Autowired
    private OrderService orderService;

    @Autowired
    private PaymentService paymentService;

    // 主流程:使用REQUIRED传播
    @Transactional(propagation = Propagation.REQUIRED)
    public void createOrder(Order order) {
        // 1. 创建用户(加入当前事务)
        userService.updateUserBalance(
            order.getUserId(), 
            order.getAmount().negate());

        // 2. 创建订单(加入当前事务)
        orderMapper.insert(order);

        // 3. 支付(加入当前事务)
        paymentService.processPayment(order);

        // 事务提交
    }

    // 支付服务:REQUIRES_NEW,独立事务
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void processPayment(Order order) {
        // 在新事务中执行
        paymentMapper.insert(order);
    }

    // 异常处理:自动回滚
    @Transactional(rollbackFor = Exception.class)
    public void transfer(Long fromId, Long toId, 
                        BigDecimal amount) {
        try {
            userService.decrease(fromId, amount);
            userService.increase(toId, amount);
            // 成功:自动提交
        } catch (Exception e) {
            // 失败:自动回滚
            throw e;
        }
    }
}

六、事务传播机制

事务传播定义了事务方法被调用时事务边界的行为。

6.1 传播行为详解

typescript 复制代码
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
    methodB();  // 加入methodA的事务
}

@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
    // ...
}
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
    methodB();  // methodB在新事务中执行
    // methodA的事务被挂起
}

@Transactional(propagation = Propagation.REQUIRES_NEW)
public void methodB() {
    // 在独立事务中执行
}
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
    methodB();  // methodB在嵌套事务中执行
    // 如果methodB失败,只回滚methodB
}

@Transactional(propagation = Propagation.NESTED)
public void methodB() {
    // 在嵌套事务中执行
}
@Service
public class TransactionalService {

    @Autowired
    private LogService logService;

    @Autowired
    private DataService dataService;

    // 主事务
    @Transactional(propagation = Propagation.REQUIRED)
    public void businessOperation() {
        try {
            // 1. 业务操作(主事务)
            dataService.insertData("业务数据");

            // 2. 记录日志(独立事务)
            logService.saveLog("操作日志");

            // 3. 发送通知(独立事务)
            logService.sendNotification("操作通知");

        } catch (Exception e) {
            // 业务失败,主事务回滚
            // 但日志记录和通知不会回滚
            // (因为使用了REQUIRES_NEW)
            throw e;
        }
    }
}

@Service
public class LogService {

    // 独立事务记录日志
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void saveLog(String message) {
        // 在新事务中执行
        // 无论主事务成功或失败,日志都会被记录
        logMapper.insert(message);
    }

    // 独立事务发送通知
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void sendNotification(String message) {
        // 在新事务中执行
        notificationMapper.insert(message);
    }
}
@Service
public class IsolationLevelService {

    // 读已提交:允许不可重复读
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public List<User> listUsers() {
        return userMapper.selectAll();
    }

    // 可重复读:防止不可重复读和幻读
    @Transactional(isolation = Isolation.REPEATABLE_READ)
    public User getUserWithLock(Long id) {
        return userMapper.selectByIdForUpdate(id);
    }

    // 串行化:最高隔离级别
    @Transactional(isolation = Isolation.SERIALIZABLE)
    public void criticalOperation() {
        // 串行执行,完全隔离
    }
}

七、最佳实践

事务传播定义了事务方法被调用时事务边界的行为。

7.1 事务边界划分

java 复制代码
✅ 事务范围要小 - 事务尽可能短,减少锁竞争
✅ 事务放在Service层 - 不在DAO层管理事务
✅ 避免长事务 - 不要在事务中进行RPC、HTTP等外部调用
✅ 明确事务边界 - 使用@Transactional清晰标注事务范围要小 - 事务尽可能短,减少锁竞争
✅ 事务放在Service层 - 不在DAO层管理事务
✅ 避免长事务 - 不要在事务中进行RPC、HTTP等外部调用
✅ 明确事务边界 - 使用@Transactional清晰标注

7.2 性能优化建议

ini 复制代码
1️⃣ 选择合适的隔离级别 - READ_COMMITTED通常足够
2️⃣ 避免事务中的外部调用 - 如网络请求、文件操作
3️⃣ 批量操作使用BatchExecutor - 提升批量操作性能
4️⃣ 合理使用只读事务 - 查询操作使用readOnly=true

7.3 常见问题解决

typescript 复制代码
// 问题:异常被捕获,事务未回滚
@Transactional
public void process() {
    try {
        // 业务代码
    } catch (Exception e) {
        e.printStackTrace(); // 捕获异常,事务不回滚
    }
}

//解决:要么抛出异常,要么手动回滚
@Transactional(rollbackFor = Exception.class)
public void process() {
    // 业务代码
}
// 查询方法使用只读事务
@Transactional(readOnly = true)
public List<User> listUsers() {
    return userMapper.selectAll();
}

//好处:
//告诉数据库这是只读操作
//数据库可以进行优化
//减少锁竞争
// 设置事务超时时间(单位:秒)
@Transactional(timeout = 30)
public void longRunningOperation() {
    // 如果方法执行超过30秒,事务自动回滚
}

MyBatis的事务管理模块提供了灵活而强大的事务控制能力。

复制代码
1️⃣ Transaction接口 - 定义事务的基本操作
2️⃣ JdbcTransaction - 使用JDBC原生方式管理事务
3️⃣ ManagedTransaction - 由容器管理事务生命周期
4️⃣ 事务提交和回滚 - 根据操作结果决定提交或回滚
5️⃣ 框架集成 - 与Spring等框架无缝集成
相关推荐
北执南念2 小时前
MyBatis 基础总结
oracle·tomcat·mybatis
千百元15 小时前
限制网段访问服务器端口63790
java·网络·mybatis
Caarlossss15 小时前
mybatis
java·数据库·tomcat·maven·mybatis·mybatis-spring
liuc031716 小时前
AI下调用redis并调用deepseek
数据库·redis·mybatis
雨中飘荡的记忆18 小时前
MyBatis缓存模块详解
mybatis
雨中飘荡的记忆1 天前
MyBatis类型处理模块详解
java·mybatis
微爱帮监所写信寄信1 天前
微爱帮监狱寄信写信小程序PHP底层优化框架
java·开发语言·数据库·spring·微信·php·mybatis
雨中飘荡的记忆2 天前
MyBatis反射模块详解
java·mybatis