SimpleExecutor、ReuseExecutor、BatchExecutor、CachingExecutor

80 阅读3分钟

MyBatis 提供了四种执行器(Executor),分别针对不同的场景优化 SQL 执行效率。以下是每种执行器的详细解释及最佳实践:

1. SimpleExecutor:简单场景

  • 工作机制

    • 默认执行器,每次执行 SQL 时创建新的 Statement 对象,执行后立即关闭。
    • 不缓存任何资源,确保每次操作都是独立的。
  • 适用场景

    • 单次查询或更新操作。
    • 不需要复用 SQL 或批量处理的常规场景。
  • 最佳实践

    • 无需特殊配置:MyBatis 默认使用 SimpleExecutor,适合大多数简单操作。
    • 避免循环中的频繁调用:若在循环中重复执行相同 SQL,应切换至 ReuseExecutor 以提升性能。

示例代码

// 默认使用 SimpleExecutor
try (SqlSession session = sqlSessionFactory.openSession()) {
    UserMapper mapper = session.getMapper(UserMapper.class);
    User user = mapper.selectUserById(1);
}

2. ReuseExecutor:高频重复 SQL

  • 工作机制

    • 复用 PreparedStatement 对象,根据 SQL 语句缓存已编译的 PreparedStatement
    • 相同 SQL 的多次执行共享同一 PreparedStatement,减少数据库编译开销。
  • 适用场景

    • 高频执行相同 SQL(如循环中多次查询)。
    • SQL 参数类型和顺序固定的场景。
  • 最佳实践

    • 启用方式:通过配置或代码指定执行器类型为 REUSE
    • 注意 SQL 一致性:SQL 必须完全相同(包括空格和参数顺序),否则无法复用。
    • 避免内存泄漏:及时关闭 SqlSession,防止缓存的 PreparedStatement 长期占用资源。

配置方式

<settings>
    <setting name="defaultExecutorType" value="REUSE"/>
</settings>

示例代码

try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.REUSE)) {
    UserMapper mapper = session.getMapper(UserMapper.class);
    for (int i = 0; i < 1000; i++) {
        User user = mapper.selectUserById(i); // 相同 SQL 复用 PreparedStatement
    }
}

3. BatchExecutor:批量操作

  • 工作机制

    • 批量模式:将多个更新操作(如 INSERT/UPDATE/DELETE)缓存到 Statement 队列,最后一次性提交。
    • 显著减少数据库交互次数,提升批量操作性能。
  • 适用场景

    • 批量插入、更新或删除(如导入数据、批量更新状态)。
  • 最佳实践

    • 显式提交事务:批量操作后必须调用 commit(),否则数据不会持久化。
    • 合理控制批量大小:避免单次批量操作数据量过大导致内存溢出。
    • 资源释放:操作完成后及时关闭 SqlSession

示例代码

try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
    UserMapper mapper = session.getMapper(UserMapper.class);
    for (User user : userList) {
        mapper.insertUser(user); // 添加至批处理队列
    }
    session.commit(); // 必须显式提交!
}

4. CachingExecutor:二级缓存支持

  • 工作机制

    • 装饰器模式:包裹其他执行器(如 SimpleExecutor),为其添加二级缓存功能。
    • 优先从缓存读取数据,未命中时委托底层执行器查询数据库,并将结果缓存。
  • 适用场景

    • 读多写少的数据(如配置信息、静态数据)。
    • 允许一定时间的数据不一致性。
  • 最佳实践

    • 启用缓存:在 Mapper XML 中添加 <cache/> 标签。
    • 序列化要求:实体类需实现 Serializable 接口。
    • 缓存失效:更新操作自动清除相关缓存,避免脏读。
    • 分布式缓存:集群环境下建议集成 Redis 或 Ehcache。

配置方式

<!-- UserMapper.xml -->
<mapper namespace="com.example.UserMapper">
    <cache/> <!-- 启用二级缓存 -->
    <select id="selectUserById" resultType="User">...</select>
</mapper>

示例代码

// 第一次查询(缓存未命中,从数据库读取)
User user1 = userMapper.selectUserById(1);
// 第二次查询(直接读取缓存)
User user2 = userMapper.selectUserById(1);

总结:执行器选择建议

执行器适用场景性能优化点
SimpleExecutor简单单次操作默认配置,无需额外处理。
ReuseExecutor高频重复 SQL减少 PreparedStatement 编译开销。
BatchExecutor批量写操作减少数据库交互次数。
CachingExecutor读多写少,允许缓存减少数据库查询压力。

通用最佳实践

  1. 根据业务场景灵活选择执行器类型。
  2. 批量操作后务必显式提交事务。
  3. 高频查询场景合理使用二级缓存,但注意缓存一致性。
  4. 生产环境中监控执行器性能,及时调整配置。