Mybatis 都有哪些 Executor 执行器?


一则或许对你有用的小广告

欢迎加入小哈的星球,你将获得:专属的实战项目(4个项目都能学) / 1v1 提问 / 简历修改 / Java 学习路线 / 社群讨论 / 学习打卡 / 每月赠书

  • 《Spring AI 项目实战(问答机器人、RAG 智能客服、联网搜索)》已完结,基于 Spring AI + Spring Boot 3.x + JDK 21...查看介绍

  • 《从零手撸:仿小红书(微服务架构)》 已完结,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...查看介绍;演示链接:http://116.62.199.48:7070/

  • 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接:http://116.62.199.48/

  • 新开坑项目:《从零手撸:秒杀系统高并发优化实战》 正在更新中...,查看介绍

截止目前,星球内专栏累计输出 150w+ 字,讲解图 5110+ 张,还在持续爆肝中.. 后续还会上新更多项目,已有 4700+ 小伙伴加入学习,欢迎点击围观

面试考察点

  1. 框架原理理解:面试官不仅仅是想知道有几种 Executor,更是想考察你是否理解 MyBatis 的 SQL 执行层设计——Executor 作为核心调度者,如何与 StatementHandler、缓存、事务协作。

  2. 差异化认知:看你能否说清楚三种 Executor 的行为差异(Statement 管理、批量执行),以及各自适用的业务场景。

  3. 生产实践意识:考察你是否知道如何选择合适的 Executor,以及 BatchExecutor 在批量场景下的正确使用方式。

核心答案

MyBatis 提供了 3 种内置的 Executor 执行器(都实现了 Executor 接口),另外还有一个装饰器 CachingExecutor

Executor 类型 行为特点 适用场景 Statement 管理
SimpleExecutor 每次 SQL 都创建新的 Statement,执行完关闭 通用场景(默认 一条一创建一关闭
ReuseExecutor 缓存 Statement,相同 SQL 复用 短时间内重复执行相同 SQL 缓存复用
BatchExecutor 通过 addBatch() 缓存 SQL,统一 executeBatch() 批量插入/更新 批量提交

一句话结论:默认使用 SimpleExecutor,批量操作用 BatchExecutorReuseExecutor 少见。无论哪种,都可以被 CachingExecutor 装饰来支持二级缓存。

深度解析

一、三种 Executor 的执行差异

三种 Executor 的核心差异在于 Statement 的生命周期管理和 SQL 执行策略

  • SimpleExecutor:最简单的实现,每次执行 SQL 都创建一个新的 PreparedStatement,执行完毕后立即关闭。好处是简单、不会有资源泄漏;缺点是如果同一条 SQL 在一次请求中被多次执行(比如循环中调用),每次都要重新创建 Statement,有额外开销。

  • ReuseExecutor:维护一个 Map<String, Statement> 缓存,以 SQL 字符串为 key。执行 SQL 时先查缓存,如果有就复用,没有才创建。Statement 不在每次执行后关闭,而是在 SqlSession 关闭时统一关闭。适合短时间内在一个 SqlSession 中多次执行相同 SQL 的场景。

  • BatchExecutor:专门为批量操作设计。执行 SQL 时不立即发送到数据库,而是通过 PreparedStatement.addBatch() 将 SQL 添加到 JDBC 的批处理缓冲区中,调用 commit()flushStatements() 时才通过 executeBatch() 一次性提交。适合需要一次性插入/更新大量数据的场景。

二、配置方式

方式一:全局配置

mybatis-config.xml 中指定默认的 Executor 类型:

<settings>
    <!-- 可选值:SIMPLE(默认)、REUSE、BATCH -->
    <setting name="defaultExecutorType" value="SIMPLE"/>
</settings>

方式二:创建 SqlSession 时指定

// 指定使用 BatchExecutor
SqlSession batchSession = sqlSessionFactory.openSession(ExecutorType.BATCH);

// 指定使用 ReuseExecutor
SqlSession reuseSession = sqlSessionFactory.openSession(ExecutorType.REUSE);

// 默认 SimpleExecutor
SqlSession simpleSession = sqlSessionFactory.openSession();

方式三:Spring 整合中配置

@Configuration
public class MyBatisConfig {

    // 全局配置:在 SqlSessionFactoryBean 中指定
    @Bean
    public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        org.apache.ibatis.session.Configuration config =
            new org.apache.ibatis.session.Configuration();
        config.setDefaultExecutorType(ExecutorType.REUSE);
        bean.setConfiguration(config);
        return bean;
    }

    // 单独创建批量 SqlSessionTemplate
    @Bean("batchSqlSessionTemplate")
    public SqlSessionTemplate batchSqlSessionTemplate(
            SqlSessionFactory sqlSessionFactory) {
        return new SqlSessionTemplate(sqlSessionFactory, ExecutorType.BATCH);
    }
}

三、CachingExecutor——二级缓存的装饰器

除了三种基本 Executor,MyBatis 还有一个 CachingExecutor,它不是一种独立的 Executor,而是一个装饰器

CachingExecutor 是装饰器模式的典型应用:

  • 当二级缓存开启(cacheEnabled = true,默认开启)时,MyBatis 会用 CachingExecutor 包装实际的 Executor(Simple/Reuse/Batch)。
  • CachingExecutor 在查询时先检查二级缓存,命中则直接返回;未命中则委托给底层 Executor 执行,执行完将结果写入二级缓存。
  • 更新操作时,CachingExecutor 会清空对应的二级缓存。

这就是为什么查询的完整顺序是 "二级缓存 → 一级缓存 → 数据库"

四、三种 Executor 的源码差异

三种 Executor 的核心区别在 doUpdate() 方法中(简化版):

// SimpleExecutor:每次都创建新 Statement
public int doUpdate(MappedStatement ms, Object parameter) {
    Statement stmt = null;
    try {
        stmt = prepareStatement(handler);  // 每次创建新的
        return handler.update(stmt);       // 立即执行
    } finally {
        closeStatement(stmt);              // 立即关闭
    }
}

// ReuseExecutor:缓存 Statement
public int doUpdate(MappedStatement ms, Object parameter) {
    Statement stmt = prepareStatement(handler);  // 先查缓存,没有才创建
    return handler.update(stmt);                  // 立即执行(不关闭)
    // Statement 保存在 Map<String, Statement> 中
}

// BatchExecutor:addBatch 缓存
public int doUpdate(MappedStatement ms, Object parameter) {
    Statement stmt = prepareStatement(handler);
    handler.batch(stmt);  // 调用 addBatch(),不执行!
    return BATCH_UPDATE_RETURN_VALUE;  // 返回常量,不是实际结果
}

关键源码差异总结:

  • SimpleExecutordoUpdate() 每次都创建 Statement、执行 SQL、然后关闭。最简单直接。
  • ReuseExecutordoUpdate() 复用缓存的 Statement,执行 SQL 但不关闭,等 SqlSession 关闭时统一清理。
  • BatchExecutordoUpdate() 只调用 addBatch()不执行 SQL,返回一个常量值。真正的执行要等到 doFlushStatements() 被调用时才发生。

五、生产环境选择建议

场景 推荐 Executor 原因
通用 CRUD SimpleExecutor(默认) 简单可靠,无资源泄漏风险
批量插入/更新 BatchExecutor addBatch() + executeBatch() 减少网络往返
短时间内重复执行相同 SQL ReuseExecutor 减少 Statement 创建开销
需要二级缓存 任意 + CachingExecutor 装饰 二级缓存是装饰器,和具体 Executor 无关

六、常见误区

  1. 误区一:"BatchExecutorSimpleExecutor 快很多"
    • 不一定。BatchExecutor 的优势在于减少网络往返次数(多条 SQL 一次提交),但如果只执行单条 SQL,性能和 SimpleExecutor 一样。而且 BatchExecutor 不支持 SELECT,所以只适合批量写操作。
  2. 误区二:"开启了 ReuseExecutor 就能大幅提升性能"
    • 提升有限。Statement 的创建开销在整个 SQL 执行耗时中占比很小(网络 IO 和 SQL 解析才是大头)。实际项目中很少用 ReuseExecutorSimpleExecutor 就够用了。
  3. 误区三:"BatchExecutorinsert() 方法会立即返回自增主键"
    • 不会。BatchExecutordoUpdate() 只调用 addBatch(),不执行 SQL,所以无法立即获取自增主键。必须调用 flushStatements() 后才能拿到。

面试高频追问

  1. 追问一:为什么默认使用 SimpleExecutor 而不是 ReuseExecutor
    • 因为 SimpleExecutor 最安全——每次用完就关闭,不会出现 Statement 泄漏问题。ReuseExecutor 缓存的 Statement 如果忘记关闭 SqlSession,会导致数据库连接泄漏。安全优先于微小的性能提升。
  2. 追问二BatchExecutor<foreach> 批量插入有什么区别?
    • <foreach> 是在 SQL 层面拼接多值 INSERT(一条 SQL 搞定),BatchExecutor 是在 JDBC 层面批量提交多条单值 INSERT。性能上 <foreach> 通常更快,但有 SQL 长度限制;BatchExecutor 没有长度限制,配合 rewriteBatchedStatements=true 后性能接近。
  3. 追问三CachingExecutor 和具体的 Executor 是什么关系?
    • 装饰器模式。CachingExecutor 持有一个 delegate(实际 Executor),在 delegate 的基础上增加了二级缓存功能。查询时先走 CachingExecutor 的二级缓存,未命中再委托给 delegate 的一级缓存和数据库。

常见面试变体

  • 变体一:"MyBatis 的 Executor 接口有哪些实现类?"
  • 变体二:"SimpleExecutorBatchExecutor 的区别?"
  • 变体三:"MyBatis 二级缓存的 CachingExecutor 是怎么实现的?"

记忆口诀

Simple 每条创建关,Reuse 缓存复用省开销,Batch 缓冲统一交,Caching 装饰加二级缓存。

总结

MyBatis 有 3 种内置 Executor:SimpleExecutor(默认,每次创建关闭 Statement)、ReuseExecutor(缓存复用 Statement)、BatchExecutor(批量 addBatch 提交)。另有 CachingExecutor 装饰器为任意 Executor 添加二级缓存能力。生产环境默认用 SimpleExecutor,批量操作切换为 BatchExecutor