MyBatis 源码解析:BatchExecutor 与 SimpleExecutor 详解

摘要

在 MyBatis 中,Executor 是执行 SQL 语句的核心组件。SimpleExecutorBatchExecutorExecutor 的两种重要实现方式:前者负责简单的 SQL 执行,后者支持批量 SQL 执行。本文将通过自定义实现 BatchExecutorSimpleExecutor,深入分析它们的工作机制,并理解它们在实际开发中的应用场景。


前言

Executor 是 MyBatis 中负责执行 SQL 语句的核心接口,它的不同实现类为不同场景提供了灵活的解决方案。SimpleExecutor 是最常用的执行器,适合简单的 SQL 操作,而 BatchExecutor 通过批量处理 SQL 语句来提高性能,适用于大批量的数据处理场景。本文将通过自定义实现这两个执行器,并对 MyBatis 源码进行详细解析,帮助读者理解它们的工作机制。


自定义实现:SimpleExecutor 与 BatchExecutor

目标与功能

我们将实现两个 Executor 类:

  1. SimpleExecutor:执行简单的增删改查操作,不做任何缓存管理。
  2. BatchExecutor:支持批量执行 SQL 操作,通过 addBatchexecuteBatch 方法处理多条 SQL 语句。

核心流程

  • SimpleExecutor:每次执行 SQL 都会打开新的数据库连接,适合单条 SQL 语句的执行场景。
  • BatchExecutor:通过批处理机制,在同一连接上执行多条 SQL 语句,提高大批量操作的执行效率。

实现过程

1. SimpleExecutor 实现

SimpleExecutor 类的功能是最基础的 SQL 执行,每次执行时都会创建新的数据库连接。

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * SimpleExecutor 是 Executor 接口的简单实现。
 * 它负责执行单条 SQL 操作,不支持批处理或缓存管理。
 */
public class SimpleExecutor implements Executor {
    private final Connection connection;

    public SimpleExecutor(Connection connection) {
        this.connection = connection;
    }

    @Override
    public <T> T query(String statement, Object parameter) {
        try (PreparedStatement pstmt = connection.prepareStatement(statement)) {
            pstmt.setObject(1, parameter);
            try (ResultSet rs = pstmt.executeQuery()) {
                if (rs.next()) {
                    // 直接返回查询结果,简化处理
                    return (T) rs.getObject(1);
                }
            }
        } catch (SQLException e) {
            throw new RuntimeException("Error executing query", e);
        }
        return null;
    }

    @Override
    public int update(String statement, Object parameter) {
        try (PreparedStatement pstmt = connection.prepareStatement(statement)) {
            pstmt.setObject(1, parameter);
            return pstmt.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException("Error executing update", e);
        }
    }

    @Override
    public void commit() {
        try {
            connection.commit();
        } catch (SQLException e) {
            throw new RuntimeException("Failed to commit transaction", e);
        }
    }

    @Override
    public void rollback() {
        try {
            connection.rollback();
        } catch (SQLException e) {
            throw new RuntimeException("Failed to rollback transaction", e);
        }
    }

    @Override
    public void close() {
        try {
            connection.close();
        } catch (SQLException e) {
            throw new RuntimeException("Failed to close connection", e);
        }
    }
}
  • query:执行 SQL 查询操作,并返回结果集的第一列数据。
  • update:执行 SQL 更新操作,包括插入、更新和删除。
  • commitrollback:处理事务提交和回滚。
  • close:关闭数据库连接,释放资源。
2. BatchExecutor 实现

BatchExecutor 通过批量执行 SQL 语句提高执行效率,适合大批量数据操作的场景。

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * BatchExecutor 实现了 Executor 接口的批处理功能。
 * 它将多条 SQL 语句批量执行,避免重复的数据库连接开销。
 */
public class BatchExecutor implements Executor {
    private final Connection connection;
    private final List<PreparedStatement> batchStatements = new ArrayList<>();

    public BatchExecutor(Connection connection) {
        this.connection = connection;
    }

    @Override
    public <T> T query(String statement, Object parameter) {
        throw new UnsupportedOperationException("BatchExecutor does not support query operation.");
    }

    @Override
    public int update(String statement, Object parameter) {
        try {
            PreparedStatement pstmt = connection.prepareStatement(statement);
            pstmt.setObject(1, parameter);
            pstmt.addBatch();
            batchStatements.add(pstmt);  // 缓存每个 PreparedStatement
            return 0;
        } catch (SQLException e) {
            throw new RuntimeException("Error adding statement to batch", e);
        }
    }

    public int[] executeBatch() {
        try {
            int[] result = new int[batchStatements.size()];
            for (PreparedStatement pstmt : batchStatements) {
                result = pstmt.executeBatch();  // 执行批量操作
            }
            return result;
        } catch (SQLException e) {
            throw new RuntimeException("Error executing batch", e);
        }
    }

    @Override
    public void commit() {
        try {
            executeBatch();  // 提交之前执行批量操作
            connection.commit();
        } catch (SQLException e) {
            throw new RuntimeException("Failed to commit batch transaction", e);
        }
    }

    @Override
    public void rollback() {
        try {
            connection.rollback();
        } catch (SQLException e) {
            throw new RuntimeException("Failed to rollback transaction", e);
        }
    }

    @Override
    public void close() {
        try {
            for (PreparedStatement pstmt : batchStatements) {
                pstmt.close();
            }
            connection.close();
        } catch (SQLException e) {
            throw new RuntimeException("Failed to close connection", e);
        }
    }
}
  • update:将 SQL 操作添加到批处理队列中,不会立即执行。
  • executeBatch:执行所有缓存的 SQL 操作。
  • commitrollback:批量提交或回滚所有操作。
  • close:关闭所有 PreparedStatement 和数据库连接,释放资源。
3. 测试 SimpleExecutor 和 BatchExecutor

我们编写测试类来验证 SimpleExecutorBatchExecutor 的功能。

import java.sql.Connection;
import java.sql.DriverManager;

public class ExecutorTest {
    public static void main(String[] args) {
        try {
            // 模拟数据库连接
            Connection connection = DriverManager.getConnection("jdbc:h2:mem:testdb", "sa", "");

            // 测试 SimpleExecutor
            SimpleExecutor simpleExecutor = new SimpleExecutor(connection);
            simpleExecutor.update("INSERT INTO users (name, age) VALUES ('Alice', 25)", null);
            Object result = simpleExecutor.query("SELECT age FROM users WHERE name = 'Alice'", null);
            System.out.println("SimpleExecutor Query result: " + result);
            simpleExecutor.commit();

            // 测试 BatchExecutor
            BatchExecutor batchExecutor = new BatchExecutor(connection);
            batchExecutor.update("INSERT INTO users (name, age) VALUES ('Bob', 30)", null);
            batchExecutor.update("INSERT INTO users (name, age) VALUES ('Charlie', 35)", null);
            batchExecutor.executeBatch();  // 执行批处理
            batchExecutor.commit();

            // 关闭资源
            simpleExecutor.close();
            batchExecutor.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

自定义实现类图

«interface»
Executor
+query(String statement, Object parameter)
+int update(String statement, Object parameter)
+commit()
+rollback()
+close()
SimpleExecutor
- Connection connection
+query(String statement, Object parameter)
+int update(String statement, Object parameter)
+commit()
+rollback()
+close()
BatchExecutor
- Connection connection
- List batchStatements
+int[] executeBatch()
+int update(String statement, Object parameter)
+commit()
+rollback()
+close()
Connection
PreparedStatement

代码解析流程图

SimpleExecutor
BatchExecutor
开始
选择执行器类型
执行 SQL 查询操作
添加 SQL 操作到批处理队列
执行批处理操作
提交或回滚事务
关闭执行器并释放资源
结束

源码解析:MyBatis 中的 SimpleExecutor 与 BatchExecutor

1. SimpleExecutor 的设计与实现

在 MyBatis 中,SimpleExecutor 是最基础的 Executor 实现类。每次执行 SQL 语句时,它会新建一个数据库连接,并且不涉及任何批量处理或缓存管理。SimpleExecutor 适合简单的 SQL 操作,例如单条数据的增删改查操作。

public class SimpleExecutor extends BaseExecutor {

    @Override
    public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
        Statement stmt = null;
        try {
            Configuration configuration = ms.getConfiguration();
            StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
            stmt = prepareStatement(handler, ms.getStatementLog());
            return handler.update(stmt);
        } finally {
            closeStatement(stmt);
        }
    }

    @Override
    public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        Statement stmt = null;
        try {
            Configuration configuration = ms.getConfiguration();
            StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
            stmt = prepareStatement(handler, ms.getStatementLog());
            return handler.query(stmt, resultHandler);
        } finally {
            closeStatement(stmt);
        }
    }

    // 其他方法...
}
  • doUpdate:负责执行 INSERTUPDATEDELETE 操作。
  • doQuery:执行 SELECT 操作,并返回查询结果。

SimpleExecutorBaseExecutor 的子类,BaseExecutor 提供了事务管理和资源关闭的基础功能。

2. BatchExecutor 的设计与实现

BatchExecutor 是 MyBatis 中用于批量处理的 Executor 实现。它的核心思想是通过 addBatchexecuteBatch 来减少数据库的往返次数,从而提升大批量数据操作的执行效率。

public class BatchExecutor extends BaseExecutor {
    private final List<Statement> statementList = new ArrayList<>();
    private final List<BatchResult> batchResultList = new ArrayList<>();

    @Override
    public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
        Statement stmt;
        BatchResult batchResult = null;
        try {
            Configuration configuration = ms.getConfiguration();
            StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
            stmt = prepareStatement(handler, ms.getStatementLog());
            handler.parameterize(stmt); // 处理参数

            // 获取缓存的 statement
            batchResult = findBatchResult(ms, parameter, handler);
            if (batchResult == null) {
                stmt.addBatch();
                batchResult = new BatchResult(ms, parameter);
                batchResultList.add(batchResult);
                statementList.add(stmt);
            }

            batchResult.addBatch(stmt);
            return -1;  // MyBatis 默认返回-1
        } catch (SQLException e) {
            throw new SQLException("Error executing batch update", e);
        }
    }

    public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
        List<BatchResult> results = new ArrayList<>();
        try {
            for (Statement stmt : statementList) {
                stmt.executeBatch();  // 执行所有的批处理操作
            }
            return results;
        } finally {
            statementList.clear();  // 清空批量处理列表
        }
    }

    // 其他方法...
}
  • doUpdate:负责批量执行 INSERTUPDATEDELETE 操作。
  • doFlushStatements:执行批量操作并返回执行结果,通常用于提交事务前。

3. 提交与回滚的事务管理

在 MyBatis 中,BatchExecutorSimpleExecutor 的事务管理是通过 BaseExecutor 统一实现的。在 commitrollback 时,BatchExecutor 会首先执行所有的批处理操作,然后再提交或回滚事务。


总结与互动

通过本文,我们详细探讨了 MyBatis 中 SimpleExecutorBatchExecutor 的设计与实现,并通过自定义实现加深了对这两类执行器的理解。SimpleExecutor 适用于简单的 SQL 操作,而 BatchExecutor 通过批量处理提高了大规模数据操作的性能。掌握这些执行器的工作机制,有助于我们在实际项目中选择合适的执行策略,优化系统的性能。

如果您觉得这篇文章对您有帮助,请点赞、收藏并关注!此外,欢迎在评论区留言,与我们分享您的见解或提出疑问!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

捕风捉你

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值