全部学科
NodeJS全栈
nodejs
Python全栈
python
小程序首页
📅 2026-05-20 8 分钟 ✍️ juanwangdev

Executor 执行器体系

Executor 是 MyBatis SQL 执行的核心组件,负责创建 Statement、设置参数、执行 SQL、处理结果集。MyBatis 提供三种 Executor 实现,各有不同的性能特点。

执行器层次结构

Java
Executor(接口)
  └── BaseExecutor(抽象基类)
        ├── SimpleExecutor    // 简单执行器,默认
        ├── ReuseExecutor     // 可复用执行器
        └── BatchExecutor     // 批处理执行器
  └── CachingExecutor(装饰器)
        └── 包装上述任一 BaseExecutor 实现二级缓存

Executor 创建过程

Java
public class Configuration {
  
  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    
    Executor executor;
    
    // 1. 根据类型创建对应 Executor
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
      executor = new SimpleExecutor(this, transaction);
    }
    
    // 2. 如果开启二级缓存,包装为 CachingExecutor
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    
    // 3. 应用插件拦截
    executor = (Executor) interceptorChain.pluginAll(executor);
    
    return executor;
  }
}

装饰器模式:CachingExecutor 不改变 Executor 接口,而是在内部持有真正的 Executor,先查二级缓存,未命中再委托给底层 Executor 执行。

BaseExecutor 核心职责

三种 Executor 都继承 BaseExecutor,共享以下核心功能:

Java
public abstract class BaseExecutor implements Executor {
  
  protected Transaction transaction;        // 事务对象
  protected Executor executor;              // 装饰器模式,指向 CachingExecutor
  protected Configuration configuration;    // 全局配置
  
  // 一级缓存(PerpetualCache 本质是 HashMap)
  protected PerpetualCache localCache;
  
  // 延迟加载参数存储
  protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;
  
  // 嵌套查询参数
  protected LocalOutputParameterIndex localOutputParameterIndex;
  
  // 抽象方法,由子类实现具体执行逻辑
  protected abstract int doUpdate(MappedStatement ms, Object parameter) throws SQLException;
  protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
  
  // 公共方法
  @Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) {
    BoundSql boundSql = ms.getBoundSql(parameter);
    
    // 创建缓存 key
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    
    // 从一级缓存获取
    List<E> list = (List<E>) localCache.getObject(key);
    if (list != null) {
      handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      return list;
    }
    
    // 缓存未命中,执行查询
    list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
    return list;
  }
  
  private <E> List<E> queryFromDatabase(...) {
    localCache.putObject(key, EXECUTION_PLACEHOLDER);  // 占位符,防止循环查询
    try {
      list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);  // 调用子类
    } finally {
      localCache.removeObject(key);
    }
    localCache.putObject(key, list);  // 放入一级缓存
    return list;
  }
}

一级缓存机制

Java
CacheKey 组成要素:
├── MappedStatement ID
├── RowBounds(分页偏移量)
├── SQL 语句(含 ? 占位符)
├── 参数值
└── Environment ID

一级缓存作用域:SqlSession 级别,不同 SqlSession 的一级缓存完全隔离。commit/update/close 操作会清空一级缓存。

SimpleExecutor 实现

Java
public class SimpleExecutor extends BaseExecutor {
  
  @Override
  public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      
      // 1. 创建 StatementHandler
      StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
      
      // 2. 创建 PreparedStatement
      stmt = prepareStatement(handler, ms.getStatementLog());
      
      // 3. 执行更新
      return handler.update(stmt);
    } finally {
      closeStatement(stmt);  // 每次执行后关闭 Statement
    }
  }
  
  @Override
  public <E> List<E> doQuery(...) throws SQLException {
    Statement stmt = null;
    try {
      Configuration configuration = ms.getConfiguration();
      StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler, boundSql);
      
      stmt = prepareStatement(handler, ms.getStatementLog());
      
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);  // 每次执行后关闭 Statement
    }
  }
}

特点:每次执行都创建新的 PreparedStatement,执行后立即关闭。适用于执行频率不高、SQL 语句不重复的场景。

ReuseExecutor 实现

Java
public class ReuseExecutor extends BaseExecutor {
  
  // 缓存 PreparedStatement
  private final Map<String, Statement> statementMap = new HashMap<>();
  
  @Override
  public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Configuration configuration = ms.getConfiguration();
    StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
    
    // 从缓存获取或创建 Statement
    Statement stmt = prepareStatement(handler, ms.getStatementLog());
    
    return handler.update(stmt);
  }
  
  @Override
  public <E> List<E> doQuery(...) throws SQLException {
    Configuration configuration = ms.getConfiguration();
    StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler, boundSql);
    
    Statement stmt = prepareStatement(handler, ms.getStatementLog());
    
    return handler.query(stmt, resultHandler);
  }
  
  @Override
  public void doFlushStatements(boolean isRollback) throws SQLException {
    // 关闭所有缓存的 Statement
    for (Statement stmt : statementMap.values()) {
      closeStatement(stmt);
    }
    statementMap.clear();  // 清空缓存
  }
  
  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    BoundSql boundSql = handler.getBoundSql();
    String sql = boundSql.getSql();
    
    // 检查是否已缓存
    if (statementMap.containsKey(sql)) {
      stmt = statementMap.get(sql);
    } else {
      Connection connection = getConnection(statementLog);
      stmt = handler.prepare(connection, transaction.getTimeout());
      statementMap.put(sql, stmt);  // 缓存
    }
    
    // 每次都需要设置参数(参数值可能不同)
    handler.parameterize(stmt);
    return stmt;
  }
}

特点:以 SQL 语句为 key 缓存 PreparedStatement,同一 SqlSession 内相同 SQL 重复使用时直接获取缓存。适用于同一 SqlSession 内多次执行相同 SQL 的场景。

注意:statementMap 的 key 是完整的 SQL 字符串,不同参数的相同语句会复用同一 PreparedStatement,但每次执行都会重新设置参数值。

BatchExecutor 实现

text
public class BatchExecutor extends BaseExecutor {
  
  // 当前批量执行的 Statement 列表
  private final List<Statement> statementList = new ArrayList<>();
  
  // 批量执行的批次信息(用于返回值追踪)
  private final List<BatchResult> batchResultList = new ArrayList<>();
  
  // 当前批量执行的 SQL
  private String currentSql;
  
  // 当前批量执行的 MappedStatement
  private MappedStatement currentStatement;
  
  @Override
  public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
    Configuration configuration = ms.getConfiguration();
    StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
    
    BoundSql boundSql = handler.getBoundSql();
    String sql = boundSql.getSql();
    
    // 检查是否可以追加到当前批次
    if (sql.equals(currentSql) && ms.equals(currentStatement)) {
      // 同一 SQL,追加到当前批次
      Statement stmt = handler.prepareStatement(transaction.getConnection());
      handler.parameterize(stmt);
      
      // 添加到批量执行队列
      int lastIndex = statementList.size() - 1;
      statementList.add(stmt);
      
      // 记录批次参数
      BatchResult batchResult = batchResultList.get(lastIndex);
      batchResult.addParameterObject(parameter);
      
      return BATCH_UPDATE_RETURN_VALUE;  // -2 表示批量执行
    } else {
      // 不同 SQL,启动新批次
      Statement stmt = handler.prepareStatement(transaction.getConnection());
      handler.parameterize(stmt);
      
      statementList.add(stmt);
      batchResultList.add(new BatchResult(ms, sql, parameter));
      
      currentSql = sql;
      currentStatement = ms;
      
      return BATCH_UPDATE_RETURN_VALUE;
    }
  }
  
  @Override
  public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
    if (isRollback) {
      return Collections.emptyList();
    }
    
    List<BatchResult> results = new ArrayList<>();
    
    try {
      for (int i = 0, n = statementList.size(); i < n; i++) {
        Statement stmt = statementList.get(i);
        BatchResult batchResult = batchResultList.get(i);
        
        // 执行批量 SQL
        int[] updateCounts = stmt.executeBatch();
        batchResult.setUpdateCounts(updateCounts);
        results.add(batchResult);
      }
      
      return results;
    } finally {
      // 关闭所有 Statement
      for (Statement stmt : statementList) {
        closeStatement(stmt);
      }
      statementList.clear();
      batchResultList.clear();
      currentSql = null;
      currentStatement = null;
    }
  }
}

特点:将相同 SQL 的多次操作累积为批量执行,通过 JDBC addBatch()executeBatch() 实现。适用于批量插入、批量更新场景。

批量提交时机:调用 sqlSession.flushStatements()sqlSession.commit() 时触发实际的批量执行。

三种执行器对比

特性SimpleExecutorReuseExecutorBatchExecutor
Statement创建每次新建SQL缓存复用按SQL分组复用
Statement关闭执行后立即SqlSession关闭时flushStatements时
内存占用最低中等(缓存Map)最高(累积批次)
适用场景单次执行同SQL多次执行批量插入/更新
性能特点创建开销大复用减少开销减少网络往返
返回值实际影响行数实际影响行数BATCH_UPDATE_RETURN_VALUE(-2)

CachingExecutor 二级缓存

text
public class CachingExecutor implements Executor {
  
  private final Executor delegate;  // 真正的执行器
  private final TransactionalCacheManager tcm = new TransactionalCacheManager();
  
  @Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) {
    BoundSql boundSql = ms.getBoundSql(parameter);
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    
    // 1. 从二级缓存获取
    List<E> list = (List<E>) tcm.getObject(cache, key);
    if (list != null) {
      return list;
    }
    
    // 2. 委托给底层执行器(会查一级缓存或执行SQL)
    list = delegate.query(ms, parameter, rowBounds, resultHandler);
    
    // 3. 放入二级缓存
    tcm.putObject(cache, key, list);
    return list;
  }
  
  @Override
  public void commit(boolean required) {
    delegate.commit(required);
    tcm.commit();  // 提交二级缓存
  }
}

二级缓存作用域:Namespace 级别,同一 namespace 的所有 SqlSession 共享。需要在 Mapper XML 中配置 <cache/> 开启。

执行器选择时序图

text
配置 ExecutorType.BATCH
         |
         v
  SqlSessionFactory.newExecutor()
         |
         +-- 创建 BatchExecutor
         |
         +-- cacheEnabled? ──Yes──> 包装为 CachingExecutor
         |
         +-- 应用插件拦截
         |
         v
   返回 Executor 实例

要点总结

  • 三种执行器:SimpleExecutor(默认,每次新建)、ReuseExecutor(SQL缓存复用)、BatchExecutor(批量执行)
  • 一级缓存在 BaseExecutor:所有执行器共享一级缓存机制,缓存 key 由 MappedStatement ID、SQL、参数等构成
  • Statement 生命周期:SimpleExecutor 执行后立即关闭,ReuseExecutor 在 SqlSession 关闭时关闭,BatchExecutor 在 flushStatements 时关闭
  • CachingExecutor 是装饰器:内部持有真正的 Executor,先查二级缓存再委托执行
  • BatchExecutor 返回值:批量执行的 update 返回 -2(BATCH_UPDATE_RETURN_VALUE),实际行数在 flushStatements 后获取
  • 插件拦截时机:所有 Executor 创建后都会经过 pluginAll() 处理,插件可以在执行前后插入逻辑

📝 发现内容有误?点击此处直接编辑

← 上一篇 多数据源配置
下一篇 → ParameterHandler 参数处理
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库