找回密码
 立即注册
首页 业界区 业界 【源码解读之 Mybatis】【核心篇】--第5篇:Executor执 ...

【源码解读之 Mybatis】【核心篇】--第5篇:Executor执行器体系详解

柏雅云 昨天 18:01
第5篇:Executor执行器体系详解

1. 学习目标确认

1.0 第4篇思考题解答

在深入学习Executor执行器体系之前,让我们先回顾并解答第4篇中提出的思考题,这将帮助我们更好地理解Executor在整个架构中的作用。
思考题1:为什么MyBatis选择JDK动态代理而非字节码增强(如ASM/CGLIB)?

答案要点

  • 接口代理特性:MyBatis的Mapper接口都是接口,JDK动态代理天然支持接口代理,无需依赖第三方库
  • 性能考虑:JDK动态代理在JVM层面有优化,性能优于字节码增强方案
  • 兼容性:JDK动态代理是Java标准库的一部分,兼容性更好,无需额外依赖
  • 简洁性:实现相对简单,代码量少,维护成本低
  • 功能满足:对于MyBatis的需求(方法调用拦截和转发),JDK动态代理完全满足
Executor的作用:Executor作为Mapper代理调用的最终执行者,负责具体的SQL执行和结果处理。
思考题2:多返回值场景下,MapperMethod如何区分集合、游标与映射类型?

答案要点

  • 返回类型检测:通过反射获取方法返回类型,判断是否为Collection、Cursor或Map类型
  • 注解识别:通过@MapKey注解识别Map类型,通过泛型参数确定Map的key类型
  • 执行路径选择:根据返回类型选择对应的SqlSession方法(selectList、selectCursor、selectMap)
  • 结果适配:MapperMethod根据方法签名进行结果类型适配和转换
  • 异常处理:对不匹配的返回类型进行异常处理和提示
与Executor的协作:Executor根据不同的执行类型(SELECT/INSERT/UPDATE/DELETE)和返回类型选择合适的执行策略。
思考题3:在开启二级缓存时,哪些因素会导致缓存失效或绕过?

答案要点

  • flushCache配置:非SELECT语句默认flushCache=true,会清空相关缓存
  • useCache配置:SELECT语句可配置useCache=false绕过缓存
  • ResultHandler使用:使用ResultHandler的查询会绕过二级缓存
  • 存储过程OUT参数:带有OUT参数的存储过程会绕过缓存
  • 事务边界:事务提交/回滚会影响TransactionalCache的缓存策略
  • 缓存Key变化:参数或配置变化导致CacheKey不同,无法命中缓存
Executor的缓存管理:CachingExecutor负责二级缓存的读写管理,SimpleExecutor等负责一级缓存的维护。
1.1 本篇学习目标

通过本文你将能够:

  • 深入理解MyBatis Executor执行器体系的设计思想和架构模式
  • 掌握BaseExecutor模板方法模式的实现原理和优势
  • 理解SimpleExecutor、ReuseExecutor、BatchExecutor的具体实现和适用场景
  • 掌握CachingExecutor装饰器模式在缓存管理中的应用
  • 了解Executor与StatementHandler、ParameterHandler、ResultSetHandler的协作关系
  • 具备自定义Executor扩展开发的能力
2. Executor执行器体系总览

2.1 执行器继承关系图

classDiagram    class Executor {                +update(MappedStatement, Object) int        +query(MappedStatement, Object, RowBounds, ResultHandler) List~E~        +query(MappedStatement, Object, RowBounds, ResultHandler, CacheKey, BoundSql) List~E~        +queryCursor(MappedStatement, Object, RowBounds) Cursor~E~        +flushStatements() List~BatchResult~        +commit(boolean) void        +rollback(boolean) void        +createCacheKey(MappedStatement, Object, RowBounds, BoundSql) CacheKey        +clearLocalCache() void        +deferLoad(MappedStatement, MetaObject, String, CacheKey, Class) void        +getTransaction() Transaction        +close(boolean) void        +isClosed() boolean    }      class BaseExecutor {                #doQuery(MappedStatement, Object, RowBounds, ResultHandler, BoundSql) List~E~        #doUpdate(MappedStatement, Object) int        #doFlushStatements(boolean) List~BatchResult~        #doQueryCursor(MappedStatement, Object, RowBounds, BoundSql) Cursor~E~        #queryFromDatabase(MappedStatement, Object, RowBounds, ResultHandler, CacheKey, BoundSql) List~E~        #localCache PerpetualCache        #localOutputParameterCache PerpetualCache        #deferredLoads List~DeferredLoad~        #queryStack int        #closed boolean    }      class SimpleExecutor {        +doQuery(MappedStatement, Object, RowBounds, ResultHandler, BoundSql) List~E~        +doUpdate(MappedStatement, Object) int        +doFlushStatements(boolean) List~BatchResult~        +doQueryCursor(MappedStatement, Object, RowBounds, BoundSql) Cursor~E~    }      class ReuseExecutor {        -statementMap Map~String,Statement~        +doQuery(MappedStatement, Object, RowBounds, ResultHandler, BoundSql) List~E~        +doUpdate(MappedStatement, Object) int        +doFlushStatements(boolean) List~BatchResult~        +doQueryCursor(MappedStatement, Object, RowBounds, BoundSql) Cursor~E~        +close(boolean) void    }      class BatchExecutor {        -statementList List~Statement~        -batchResultList List~BatchResult~        -currentSql String        -currentStatement MappedStatement        +doQuery(MappedStatement, Object, RowBounds, ResultHandler, BoundSql) List~E~        +doUpdate(MappedStatement, Object) int        +doFlushStatements(boolean) List~BatchResult~        +doQueryCursor(MappedStatement, Object, RowBounds, BoundSql) Cursor~E~        +close(boolean) void    }      class CachingExecutor {        -delegate Executor        -transactionalCacheManager TransactionalCacheManager        +query(MappedStatement, Object, RowBounds, ResultHandler, CacheKey, BoundSql) List~E~        +update(MappedStatement, Object) int        +commit(boolean) void        +rollback(boolean) void        +flushStatements() List~BatchResult~        +getTransaction() Transaction        +close(boolean) void        +isClosed() boolean        -ensureNoOutParams(MappedStatement, BoundSql) void        -flushCacheIfRequired(MappedStatement) void    }      Executor Executor: createCacheKey(ms, param, rowBounds, boundSql)    Executor->>Executor: queryFromDatabase(ms, param, rowBounds, handler, key, boundSql)    Executor->>SH: newStatementHandler(wrapper, ms, param, rowBounds, handler, boundSql)    SH->>SH: prepare(connection, timeout)    SH->>H: parameterize(statement)    PH->>DB: set parameters    SH->>DB: execute query    DB-->>SH: ResultSet    SH->>RSH: handleResultSets(statement)    RSH-->>SH: List    SH-->>Executor: results    Executor-->>SqlSession: List3. BaseExecutor抽象基类深度解析

3.1 模板方法模式实现

BaseExecutor采用模板方法模式,定义了SQL执行的标准流程,子类只需实现具体的执行逻辑:
  1. package org.apache.ibatis.executor;
  2. /**
  3. * 执行器抽象基类,采用模板方法模式
  4. * 定义SQL执行的标准流程,子类实现具体的执行逻辑
  5. */
  6. public abstract class BaseExecutor implements Executor {
  7.     // 事务管理器,用于管理数据库事务
  8.     protected Transaction transaction;
  9.     // 执行器包装器,通常是CachingExecutor
  10.     protected Executor wrapper;
  11.     // 一级缓存(本地缓存),session级别缓存
  12.     protected PerpetualCache localCache;
  13.     // 存储过程出参缓存
  14.     protected PerpetualCache localOutputParameterCache;
  15.     // MyBatis全局配置对象
  16.     protected Configuration configuration;
  17.     // 查询栈深度,用于控制嵌套查询
  18.     protected int queryStack;
  19.     // 执行器是否已关闭
  20.     private boolean closed;
  21.     // 延迟加载队列,存储需要延迟加载的对象
  22.     protected List<DeferredLoad> deferredLoads = new ArrayList<>();
  23.   
  24.     /**
  25.      * 模板方法:查询操作的标准流程(重载方法1)
  26.      * @param ms SQL映射语句
  27.      * @param parameter 查询参数
  28.      * @param rowBounds 分页参数
  29.      * @param resultHandler 结果处理器
  30.      * @return 查询结果列表
  31.      */
  32.     @Override
  33.     public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
  34.         // 根据参数生成动态SQL
  35.         BoundSql boundSql = ms.getBoundSql(parameter);
  36.         // 创建缓存键,用于一级缓存
  37.         CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
  38.         // 调用重载方法继续执行
  39.         return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
  40.     }
  41.   
  42.     /**
  43.      * 模板方法:查询操作的标准流程(重载方法2)
  44.      * 这是查询的核心方法,包含完整的缓存和执行逻辑
  45.      */
  46.     @Override
  47.     public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException {
  48.         // 设置错误上下文,便于调试和错误定位
  49.         ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
  50.       
  51.         // 检查执行器是否已关闭
  52.         if (closed) {
  53.             throw new ExecutorException("Executor was closed.");
  54.         }
  55.       
  56.         // 如果是最外层查询且配置了刷新缓存,则清空本地缓存
  57.         if (queryStack == 0 && ms.isFlushCacheRequired()) {
  58.             clearLocalCache();
  59.         }
  60.       
  61.         List<E> list;
  62.         try {
  63.             // 查询栈深度+1,用于处理嵌套查询
  64.             queryStack++;
  65.          
  66.             // 尝试从一级缓存中获取结果(仅当没有结果处理器时)
  67.             list = resultHandler == null ? (List<E>) localCache.getObject(cacheKey) : null;
  68.          
  69.             if (list != null) {
  70.                 // 缓存命中,处理存储过程的输出参数
  71.                 handleLocallyCachedOutputParameters(ms, cacheKey, parameter, boundSql);
  72.             } else {
  73.                 // 缓存未命中,从数据库查询
  74.                 list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
  75.             }
  76.         } finally {
  77.             // 查询栈深度-1
  78.             queryStack--;
  79.         }
  80.       
  81.         // 如果回到最外层查询
  82.         if (queryStack == 0) {
  83.             // 执行所有延迟加载
  84.             for (DeferredLoad deferredLoad : deferredLoads) {
  85.                 deferredLoad.load();
  86.             }
  87.             // 清空延迟加载队列
  88.             deferredLoads.clear();
  89.          
  90.             // 如果配置为STATEMENT级别缓存,执行完成后清空缓存
  91.             if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
  92.                 clearLocalCache();
  93.             }
  94.         }
  95.         return list;
  96.     }
  97.   
  98.     /**
  99.      * 抽象方法:子类实现具体的数据库查询逻辑
  100.      * 不同的执行器有不同的实现策略
  101.      */
  102.     protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException;
  103.   
  104.     /**
  105.      * 模板方法:更新操作的标准流程
  106.      * 包括插入、更新、删除操作
  107.      */
  108.     @Override
  109.     public int update(MappedStatement ms, Object parameter) throws SQLException {
  110.         // 设置错误上下文
  111.         ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
  112.       
  113.         // 检查执行器是否已关闭
  114.         if (closed) {
  115.             throw new ExecutorException("Executor was closed.");
  116.         }
  117.       
  118.         // 清空本地缓存(更新操作会影响缓存一致性)
  119.         clearLocalCache();
  120.       
  121.         // 调用子类的具体实现
  122.         return doUpdate(ms, parameter);
  123.     }
  124.   
  125.     /**
  126.      * 抽象方法:子类实现具体的数据库更新逻辑
  127.      */
  128.     protected abstract int doUpdate(MappedStatement ms, Object parameter) throws SQLException;
  129. }
复制代码
3.2 一级缓存机制

BaseExecutor实现了一级缓存(本地缓存),提升重复查询的性能:
  1. /**
  2. * 从数据库查询数据并缓存结果
  3. * 这是一级缓存的核心实现方法
  4. */
  5. private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException {
  6.     List<E> list;
  7.   
  8.     // 在缓存中放入占位符,防止循环引用
  9.     localCache.putObject(cacheKey, EXECUTION_PLACEHOLDER);
  10.   
  11.     try {
  12.         // 调用子类的具体实现执行数据库查询
  13.         list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
  14.     } finally {
  15.         // 无论成功还是失败,都要移除占位符
  16.         localCache.removeObject(cacheKey);
  17.     }
  18.   
  19.     // 将查询结果放入一级缓存
  20.     localCache.putObject(cacheKey, list);
  21.   
  22.     // 如果是存储过程调用,需要缓存输出参数
  23.     if (ms.getStatementType() == StatementType.CALLABLE) {
  24.         localOutputParameterCache.putObject(cacheKey, parameter);
  25.     }
  26.   
  27.     return list;
  28. }
  29. /**
  30. * 创建缓存键
  31. * 缓存键由多个因素组成,确保唯一性
  32. */
  33. @Override
  34. public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
  35.     // 检查执行器是否已关闭
  36.     if (closed) {
  37.         throw new ExecutorException("Executor was closed.");
  38.     }
  39.   
  40.     // 创建缓存键对象
  41.     CacheKey cacheKey = new CacheKey();
  42.   
  43.     // 添加SQL映射语句ID到缓存键中
  44.     cacheKey.update(ms.getId());
  45.   
  46.     // 添加分页参数到缓存键中
  47.     cacheKey.update(rowBounds.getOffset());
  48.     cacheKey.update(rowBounds.getLimit());
  49.   
  50.     // 添加SQL语句到缓存键中
  51.     cacheKey.update(boundSql.getSql());
  52.   
  53.     // 获取参数映射列表
  54.     List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
  55.     TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
  56.   
  57.     // 遍历所有参数,将参数值添加到缓存键中
  58.     for (ParameterMapping parameterMapping : parameterMappings) {
  59.         // 只处理输入参数,不处理输出参数
  60.         if (parameterMapping.getMode() != ParameterMode.OUT) {
  61.             Object value;
  62.             String propertyName = parameterMapping.getProperty();
  63.          
  64.             // 获取参数值的逐个判断逻辑
  65.             if (boundSql.hasAdditionalParameter(propertyName)) {
  66.                 // 从额外参数中获取
  67.                 value = boundSql.getAdditionalParameter(propertyName);
  68.             } else if (parameterObject == null) {
  69.                 // 参数对象为空
  70.                 value = null;
  71.             } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
  72.                 // 参数对象有对应的类型处理器(基本类型)
  73.                 value = parameterObject;
  74.             } else {
  75.                 // 复杂对象,通过反射获取属性值
  76.                 MetaObject metaObject = configuration.newMetaObject(parameterObject);
  77.                 value = metaObject.getValue(propertyName);
  78.             }
  79.          
  80.             // 将参数值添加到缓存键中
  81.             cacheKey.update(value);
  82.         }
  83.     }
  84.   
  85.     // 将环境ID添加到缓存键中(区分不同环境)
  86.     if (configuration.getEnvironment() != null) {
  87.         cacheKey.update(configuration.getEnvironment().getId());
  88.     }
  89.   
  90.     return cacheKey;
  91. }
复制代码
3.3 延迟加载机制

BaseExecutor支持延迟加载(懒加载),通过DeferredLoad实现:
  1. /**
  2. * 延迟加载类,用于实现懒加载功能
  3. * 当访问某个属性时,才真正执行相关查询
  4. */
  5. public class DeferredLoad {
  6.     // 结果对象的元数据包装器,用于反射操作
  7.     private final MetaObject resultObject;
  8.     // 需要延迟加载的属性名
  9.     private final String property;
  10.     // 目标类型(属性的类型)
  11.     private final Class<?> targetType;
  12.     // 缓存键,用于标识该次查询
  13.     private final CacheKey key;
  14.     // SQL映射语句,包含延迟加载的SQL信息
  15.     private final MappedStatement mappedStatement;
  16.     // 执行器,用于执行延迟加载查询
  17.     private final Executor executor;
  18.   
  19.     /**
  20.      * 构造延迟加载对象
  21.      */
  22.     public DeferredLoad(MetaObject resultObject, String property, Class<?> targetType, CacheKey key, MappedStatement mappedStatement, Executor executor) {
  23.         this.resultObject = resultObject;
  24.         this.property = property;
  25.         this.targetType = targetType;
  26.         this.key = key;
  27.         this.mappedStatement = mappedStatement;
  28.         this.executor = executor;
  29.     }
  30.   
  31.     /**
  32.      * 执行延迟加载
  33.      * 这个方法会在适当的时机被调用,执行实际的数据库查询
  34.      */
  35.     public void load() throws SQLException {
  36.         // 检查属性是否已经有值,如果已经有值则不需要加载
  37.         if (resultObject.getValue(property) != null) {
  38.             return;
  39.         }
  40.       
  41.         // 执行实际的数据库查询
  42.         List<Object> list = (List<Object>) executor.query(
  43.             mappedStatement,
  44.             key.getParameterObject(),
  45.             RowBounds.DEFAULT,
  46.             Executor.NO_RESULT_HANDLER,
  47.             key,
  48.             mappedStatement.getBoundSql(key.getParameterObject())
  49.         );
  50.       
  51.         // 处理查询结果
  52.         if (list != null && list.size() > 0) {
  53.             if (list.size() > 1) {
  54.                 // 多个结果,设置为列表
  55.                 resultObject.setValue(property, list);
  56.             } else {
  57.                 // 单个结果,设置为单个对象
  58.                 resultObject.setValue(property, list.get(0));
  59.             }
  60.         }
  61.     }
  62. }
复制代码
4. SimpleExecutor简单执行器

4.1 核心实现

SimpleExecutor是最基础的执行器,每次执行都创建新的Statement:
  1. package org.apache.ibatis.executor;
  2. /**
  3. * 简单执行器:MyBatis的默认执行器
  4. * 特点:每次执行都创建新的Statement对象
  5. * 优点:简单可靠,资源管理清晰
  6. * 缺点:每次都要创建新Statement,有一定性能开销
  7. */
  8. public class SimpleExecutor extends BaseExecutor {
  9.   
  10.     /**
  11.      * 构造方法
  12.      * @param configuration MyBatis全局配置
  13.      * @param transaction 事务管理器
  14.      */
  15.     public SimpleExecutor(Configuration configuration, Transaction transaction) {
  16.         super(configuration, transaction);
  17.     }
  18.   
  19.     /**
  20.      * 执行查询操作
  21.      * 每次查询都会创建新的Statement对象
  22.      */
  23.     @Override
  24.     public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
  25.         Statement stmt = null;
  26.         try {
  27.             // 获取全局配置
  28.             Configuration configuration = ms.getConfiguration();
  29.          
  30.             // 创建 StatementHandler,用于处理 SQL 语句
  31.             StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
  32.          
  33.             // 准备 Statement(创建并设置参数)
  34.             stmt = prepareStatement(handler, ms.getStatementLog());
  35.          
  36.             // 执行查询并返回结果
  37.             return handler.query(stmt, resultHandler);
  38.         } finally {
  39.             // 无论成功还是失败,都要关闭 Statement
  40.             closeStatement(stmt);
  41.         }
  42.     }
  43.   
  44.     /**
  45.      * 执行更新操作(包括 INSERT、UPDATE、DELETE)
  46.      */
  47.     @Override
  48.     public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
  49.         Statement stmt = null;
  50.         try {
  51.             // 获取全局配置
  52.             Configuration configuration = ms.getConfiguration();
  53.          
  54.             // 创建 StatementHandler
  55.             StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
  56.          
  57.             // 准备 Statement
  58.             stmt = prepareStatement(handler, ms.getStatementLog());
  59.          
  60.             // 执行更新并返回影响行数
  61.             return handler.update(stmt);
  62.         } finally {
  63.             // 关闭 Statement
  64.             closeStatement(stmt);
  65.         }
  66.     }
  67.   
  68.     /**
  69.      * 刷新批量操作
  70.      * SimpleExecutor 不支持批量操作,返回空列表
  71.      */
  72.     @Override
  73.     public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
  74.         return Collections.emptyList();
  75.     }
  76.   
  77.     /**
  78.      * 执行游标查询
  79.      * 游标查询适用于大结果集的流式处理
  80.      */
  81.     @Override
  82.     public <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException {
  83.         // 获取全局配置
  84.         Configuration configuration = ms.getConfiguration();
  85.       
  86.         // 创建 StatementHandler
  87.         StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, null, boundSql);
  88.       
  89.         // 准备 Statement
  90.         Statement stmt = prepareStatement(handler, ms.getStatementLog());
  91.       
  92.         // 返回游标对象(注意:游标不在这里关闭,由调用者负责关闭)
  93.         return handler.queryCursor(stmt);
  94.     }
  95.   
  96.     /**
  97.      * 准备 Statement对象
  98.      * 包括创建、设置超时、设置参数等操作
  99.      */
  100.     private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
  101.         Statement stmt;
  102.       
  103.         // 获取数据库连接
  104.         Connection connection = getConnection(statementLog);
  105.       
  106.         // 创建 Statement 并设置超时时间
  107.         stmt = handler.prepare(connection, transaction.getTimeout());
  108.       
  109.         // 设置 SQL 参数
  110.         handler.parameterize(stmt);
  111.       
  112.         return stmt;
  113.     }
  114.   
  115.     /**
  116.      * 关闭 Statement对象
  117.      * 安全关闭,忽略异常
  118.      */
  119.     private void closeStatement(Statement stmt) {
  120.         if (stmt != null) {
  121.             try {
  122.                 stmt.close();
  123.             } catch (SQLException e) {
  124.                 // 忽略关闭异常,避免影响主逻辑
  125.             }
  126.         }
  127.     }
  128. }
复制代码
4.2 特点分析

优势

  • 简单可靠:逻辑简单,易于理解和维护
  • 资源管理:及时释放Statement资源,避免内存泄漏
  • 线程安全:每次执行创建新Statement,无状态共享问题
  • 适用广泛:适合大多数业务场景
劣势

  • 性能开销:每次执行都创建新Statement,有一定性能开销
  • 重复工作:相同SQL的重复执行无法复用Statement
5. ReuseExecutor重用执行器

5.1 核心实现

ReuseExecutor通过重用Statement对象来提升性能:
  1. package org.apache.ibatis.executor;
  2. /**
  3. * 重用执行器:通过重用Statement对象来提升性能
  4. * 特点:相同SQL会重用同一个Statement对象
  5. * 优点:减少Statement创建开销,提升性能
  6. * 适用场景:重复执行相同SQL的场景
  7. */
  8. public class ReuseExecutor extends BaseExecutor {
  9.     // Statement缓存Map,以SQL为键,Statement为值
  10.     private final Map<String, Statement> statementMap = new HashMap<>();
  11.   
  12.     /**
  13.      * 构造方法
  14.      */
  15.     public ReuseExecutor(Configuration configuration, Transaction transaction) {
  16.         super(configuration, transaction);
  17.     }
  18.   
  19.     /**
  20.      * 执行查询操作
  21.      * 会尝试重用已存在的Statement
  22.      */
  23.     @Override
  24.     public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
  25.         Configuration configuration = ms.getConfiguration();
  26.         StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
  27.       
  28.         // 准备Statement(可能会重用已存在的)
  29.         Statement stmt = prepareStatement(handler, ms.getStatementLog());
  30.       
  31.         return handler.query(stmt, resultHandler);
  32.     }
  33.   
  34.     /**
  35.      * 执行更新操作
  36.      */
  37.     @Override
  38.     public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
  39.         Configuration configuration = ms.getConfiguration();
  40.         StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
  41.       
  42.         // 准备Statement
  43.         Statement stmt = prepareStatement(handler, ms.getStatementLog());
  44.       
  45.         return handler.update(stmt);
  46.     }
  47.   
  48.     /**
  49.      * 刷新批量操作
  50.      * 关闭所有缓存的Statement并清空缓存
  51.      */
  52.     @Override
  53.     public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
  54.         // 关闭所有缓存的Statement
  55.         for (Statement stmt : statementMap.values()) {
  56.             closeStatement(stmt);
  57.         }
  58.         // 清空缓存
  59.         statementMap.clear();
  60.         return Collections.emptyList();
  61.     }
  62.   
  63.     /**
  64.      * 执行游标查询
  65.      */
  66.     @Override
  67.     public <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException {
  68.         Configuration configuration = ms.getConfiguration();
  69.         StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, null, boundSql);
  70.         Statement stmt = prepareStatement(handler, ms.getStatementLog());
  71.         return handler.queryCursor(stmt);
  72.     }
  73.   
  74.     /**
  75.      * 准备Statement对象
  76.      * 核心方法:实现Statement的重用逻辑
  77.      */
  78.     private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
  79.         Statement stmt;
  80.         BoundSql boundSql = handler.getBoundSql();
  81.         String sql = boundSql.getSql();
  82.       
  83.         // 检查是否已经有可用的Statement
  84.         if (hasStatementFor(sql)) {
  85.             // 重用已存在的Statement
  86.             stmt = getStatement(sql);
  87.             // 重新设置事务超时时间
  88.             applyTransactionTimeout(stmt);
  89.         } else {
  90.             // 创建新的Statement
  91.             Connection connection = getConnection(statementLog);
  92.             stmt = handler.prepare(connection, transaction.getTimeout());
  93.             // 将新创建的Statement放入缓存
  94.             putStatement(sql, stmt);
  95.         }
  96.       
  97.         // 设置参数
  98.         handler.parameterize(stmt);
  99.         return stmt;
  100.     }
  101.   
  102.     /**
  103.      * 检查是否已经有可用的Statement
  104.      * @param sql SQL语句
  105.      * @return 是否存在可用的Statement
  106.      */
  107.     private boolean hasStatementFor(String sql) {
  108.         try {
  109.             // 检查缓存中是否存在该SQL对应的Statement,且Statement未关闭
  110.             return statementMap.containsKey(sql) && !statementMap.get(sql).isClosed();
  111.         } catch (SQLException e) {
  112.             // 如果检查过程中出现异常,认为不可用
  113.             return false;
  114.         }
  115.     }
  116.   
  117.     /**
  118.      * 从缓存中获取Statement
  119.      */
  120.     private Statement getStatement(String s) {
  121.         return statementMap.get(s);
  122.     }
  123.   
  124.     /**
  125.      * 将Statement放入缓存
  126.      */
  127.     private void putStatement(String sql, Statement stmt) {
  128.         statementMap.put(sql, stmt);
  129.     }
  130.   
  131.     /**
  132.      * 关闭执行器
  133.      * 需要清理所有缓存的Statement
  134.      */
  135.     @Override
  136.     public void close(boolean forceRollback) {
  137.         try {
  138.             // 先刷新批量操作
  139.             doFlushStatements(forceRollback);
  140.         } finally {
  141.             // 关闭所有Statement并清空缓存
  142.             for (Statement stmt : statementMap.values()) {
  143.                 closeStatement(stmt);
  144.             }
  145.             statementMap.clear();
  146.             // 调用父类的关闭方法
  147.             super.close(forceRollback);
  148.         }
  149.     }
  150. }
复制代码
5.2 适用场景

最佳场景

  • 循环查询:在循环中执行相同的SQL语句
  • 重复调用:同一个Mapper方法被频繁调用
  • 报表查询:生成报表时执行大量相同结构的查询
性能提升

  • 减少Statement创建开销:避免重复创建Statement对象
  • 减少SQL解析开销:重用已解析的Statement
  • 提升执行效率:特别是在高并发场景下
6. BatchExecutor批量执行器

6.1 核心实现

BatchExecutor通过批量执行多个SQL来提升批量操作的性能:
  1. package org.apache.ibatis.executor;
  2. public class BatchExecutor extends BaseExecutor {
  3.     private final List<Statement> statementList = new ArrayList<>();
  4.     private final List<BatchResult> batchResultList = new ArrayList<>();
  5.     private String currentSql;
  6.     private MappedStatement currentStatement;
  7.   
  8.     public BatchExecutor(Configuration configuration, Transaction transaction) {
  9.         super(configuration, transaction);
  10.     }
  11.   
  12.     /**
  13.      * 执行更新操作(批量模式)
  14.      * 核心方法:将多个相同SQL的更新操作打包成批量执行
  15.      */
  16.     @Override
  17.     public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
  18.         final Configuration configuration = ms.getConfiguration();
  19.         final StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
  20.         final BoundSql boundSql = handler.getBoundSql();
  21.         final String sql = boundSql.getSql();
  22.         final StatementType statementType = ms.getStatementType();
  23.       
  24.         // 检查是否可以重用当前的Statement(相同SQL和语句类型)
  25.         if (sql.equals(currentSql) && statementType == currentStatement.getStatementType()) {
  26.             // 相同SQL,重用Statement
  27.             final Statement stmt = statementList.get(statementList.size() - 1);
  28.             applyTransactionTimeout(stmt);
  29.          
  30.             // 设置参数
  31.             handler.parameterize(stmt);
  32.          
  33.             // 将参数添加到批量结果中
  34.             BatchResult batchResult = batchResultList.get(batchResultList.size() - 1);
  35.             batchResult.addParameterObject(parameter);
  36.          
  37.             return BATCH_UPDATE_RETURN_VALUE;
  38.         } else {
  39.             // 不同SQL,创建新Statement
  40.             final Statement stmt;
  41.          
  42.             // 再次检查是否可以重用(双重检查机制)
  43.             if (sql.equals(currentSql) && ms.getStatementType() == currentStatement.getStatementType()) {
  44.                 int last = statementList.size() - 1;
  45.                 stmt = statementList.get(last);
  46.                 applyTransactionTimeout(stmt);
  47.                 handler.parameterize(stmt);
  48.                 BatchResult batchResult = batchResultList.get(last);
  49.                 batchResult.addParameterObject(parameter);
  50.             } else {
  51.                 // 创建全新的Statement
  52.                 Connection connection = getConnection(ms.getStatementLog());
  53.                 stmt = handler.prepare(connection, transaction.getTimeout());
  54.                 handler.parameterize(stmt);
  55.               
  56.                 // 更新当前SQL和语句信息
  57.                 currentSql = sql;
  58.                 currentStatement = ms;
  59.               
  60.                 // 将Statement和结果对象添加到列表中
  61.                 statementList.add(stmt);
  62.                 batchResultList.add(new BatchResult(ms, sql, parameter));
  63.             }
  64.          
  65.             // 将操作添加到批量中(但不立即执行)
  66.             handler.batch(stmt);
  67.          
  68.             return BATCH_UPDATE_RETURN_VALUE;
  69.         }
  70.     }
  71.   
  72.     @Override
  73.     public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
  74.         Statement stmt = null;
  75.         try {
  76.             flushStatements();
  77.             Configuration configuration = ms.getConfiguration();
  78.             StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
  79.             Connection connection = getConnection(ms.getStatementLog());
  80.             stmt = handler.prepare(connection, transaction.getTimeout());
  81.             handler.parameterize(stmt);
  82.             return handler.query(stmt, resultHandler);
  83.         } finally {
  84.             closeStatement(stmt);
  85.         }
  86.     }
  87.   
  88.     /**
  89.      * 刷新批量操作 - 执行所有缓存的批量操作
  90.      * 这是批量执行器的核心方法,实际执行所有缓存的SQL操作
  91.      */
  92.     @Override
  93.     public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
  94.         try {
  95.             List<BatchResult> results = new ArrayList<>();
  96.          
  97.             // 如果是回滚操作,直接返回空列表
  98.             if (isRollback) {
  99.                 return Collections.emptyList();
  100.             }
  101.          
  102.             // 遍历所有缓存的Statement
  103.             for (int i = 0, n = statementList.size(); i < n; i++) {
  104.                 Statement stmt = statementList.get(i);
  105.               
  106.                 // 重新设置事务超时时间
  107.                 applyTransactionTimeout(stmt);
  108.               
  109.                 try {
  110.                     // 执行批量操作,返回影响行数数组
  111.                     int updateCount = stmt.executeBatch();
  112.                   
  113.                     // 获取对应的批量结果对象
  114.                     BatchResult batchResult = batchResultList.get(i);
  115.                   
  116.                     // 设置影响行数
  117.                     batchResult.setUpdateCounts(new int[]{updateCount});
  118.                   
  119.                     // 添加到结果列表中
  120.                     results.add(batchResult);
  121.                   
  122.                 } catch (BatchUpdateException e) {
  123.                     // 批量执行异常处理
  124.                     StringBuilder message = new StringBuilder();
  125.                     message.append(batchResultList.get(i).getSql()).append(" (failed)\n");
  126.                   
  127.                     if (e.getMessage() != null && e.getMessage().length() > 0) {
  128.                         message.append("  Cause: ").append(e.getMessage());
  129.                     }
  130.                   
  131.                     // 抛出自定义批量执行异常,包含已成功的结果
  132.                     throw new BatchExecutorException(message.toString(), e, results, batchResultList.get(i));
  133.                 }
  134.             }
  135.          
  136.             return results;
  137.          
  138.         } finally {
  139.             // 无论成功还是失败,都要清理资源
  140.          
  141.             // 关闭所有Statement
  142.             for (Statement stmt : statementList) {
  143.                 closeStatement(stmt);
  144.             }
  145.          
  146.             // 清空所有缓存列表
  147.             statementList.clear();
  148.             batchResultList.clear();
  149.          
  150.             // 重置当前状态
  151.             currentSql = null;
  152.             currentStatement = null;
  153.         }
  154.     }
  155. }
复制代码
6.2 批量操作优势

性能提升

  • 减少网络往返:多个SQL一次性发送到数据库
  • 减少事务开销:批量操作在同一个事务中完成
  • 提升吞吐量:特别适合大量数据的批量处理
适用场景

  • 批量插入:大量数据的批量插入操作
  • 批量更新:相同条件的批量更新操作
  • 批量删除:大量数据的批量删除操作
  • 数据迁移:数据导入导出场景
7. CachingExecutor缓存装饰器

7.1 装饰器模式实现

CachingExecutor采用装饰器模式,为其他执行器添加二级缓存功能:
  1. package org.apache.ibatis.executor;
  2. /**
  3. * 缓存执行器 - 采用装饰器模式实现二级缓存
  4. * 特点:为其他执行器添加二级缓存功能
  5. * 优点:大幅提升查询性能,减少数据库访问
  6. * 适用场景:需要缓存的查询场景
  7. */
  8. public class CachingExecutor implements Executor {
  9.     // 被装饰的执行器(可以是任意类型的执行器)
  10.     private final Executor delegate;
  11.     // 事务缓存管理器,管理二级缓存的事务性
  12.     private final TransactionalCacheManager tcm = new TransactionalCacheManager();
  13.   
  14.     /**
  15.      * 构造方法 - 装饰器模式的典型实现
  16.      * @param delegate 被装饰的执行器
  17.      */
  18.     public CachingExecutor(Executor delegate) {
  19.         this.delegate = delegate;
  20.         // 设置装饰器引用,便于被装饰者访问装饰器
  21.         delegate.setExecutorWrapper(this);
  22.     }
  23.   
  24.     /**
  25.      * 查询方法 - 二级缓存的核心实现
  26.      * 先检查二级缓存,未命中再委托给被装饰的执行器
  27.      */
  28.     @Override
  29.     public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
  30.         // 获取当前映射语句的缓存对象
  31.         Cache cache = ms.getCache();
  32.       
  33.         // 只有配置了缓存的语句才会使用二级缓存
  34.         if (cache != null) {
  35.             // 检查是否需要刷新缓存(更新操作会刷新缓存)
  36.             flushCacheIfRequired(ms);
  37.          
  38.             // 检查是否启用缓存且没有结果处理器
  39.             if (ms.isUseCache() && resultHandler == null) {
  40.                 // 确保没有输出参数(存储过程的OUT参数不能缓存)
  41.                 ensureNoOutParams(ms, boundSql);
  42.               
  43.                 // 尝试从二级缓存中获取结果
  44.                 @SuppressWarnings("unchecked")
  45.                 List<E> list = (List<E>) tcm.getObject(cache, key);
  46.               
  47.                 if (list == null) {
  48.                     // 缓存未命中,委托给被装饰的执行器执行查询
  49.                     list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  50.                   
  51.                     // 将查询结果放入二级缓存
  52.                     tcm.putObject(cache, key, list);
  53.                 }
  54.               
  55.                 return list;
  56.             }
  57.         }
  58.       
  59.         // 没有配置缓存或不符合缓存条件,直接委托执行
  60.         return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  61.     }
  62.   
  63.     /**
  64.      * 更新方法 - 处理更新操作对缓存的影响
  65.      */
  66.     @Override
  67.     public int update(MappedStatement ms, Object parameterObject) throws SQLException {
  68.         // 更新操作前先刷新相关缓存
  69.         flushCacheIfRequired(ms);
  70.       
  71.         // 委托给被装饰的执行器执行更新
  72.         return delegate.update(ms, parameterObject);
  73.     }
  74.   
  75.     /**
  76.      * 提交事务 - 同时提交缓存事务
  77.      */
  78.     @Override
  79.     public void commit(boolean required) throws SQLException {
  80.         // 先提交数据库事务
  81.         delegate.commit(required);
  82.       
  83.         // 再提交缓存事务(将缓存的数据真正写入缓存)
  84.         tcm.commit();
  85.     }
  86.   
  87.     /**
  88.      * 回滚事务 - 同时回滚缓存事务
  89.      */
  90.     @Override
  91.     public void rollback(boolean required) throws SQLException {
  92.         try {
  93.             // 先回滚数据库事务
  94.             delegate.rollback(required);
  95.         } finally {
  96.             // 确保缓存事务也被回滚
  97.             if (required) {
  98.                 tcm.rollback();
  99.             }
  100.         }
  101.     }
  102.   
  103.     /**
  104.      * 刷新语句 - 委托给被装饰的执行器
  105.      */
  106.     @Override
  107.     public void flushStatements() throws SQLException {
  108.         delegate.flushStatements();
  109.     }
  110.   
  111.     /**
  112.      * 根据需要刷新缓存
  113.      * 如果映射语句配置了flushCache=true,则清空缓存
  114.      */
  115.     private void flushCacheIfRequired(MappedStatement ms) {
  116.         Cache cache = ms.getCache();
  117.         // 检查是否有缓存且需要刷新
  118.         if (cache != null && ms.isFlushCacheRequired()) {
  119.             tcm.clear(cache);
  120.         }
  121.     }
  122.   
  123.     /**
  124.      * 确保没有输出参数
  125.      * 存储过程的OUT参数不能被缓存,因为每次调用结果都可能不同
  126.      */
  127.     private void ensureNoOutParams(MappedStatement ms, BoundSql boundSql) {
  128.         if (ms.getStatementType() == StatementType.CALLABLE) {
  129.             for (ParameterMapping parameterMapping : boundSql.getParameterMappings()) {
  130.                 if (parameterMapping.getMode() != ParameterMode.IN) {
  131.                     throw new ExecutorException("Caching stored procedures with OUT params is not supported. Please configure useCache=false in " + ms.getId() + " statement.");
  132.                 }
  133.             }
  134.         }
  135.     }
  136. }
复制代码
7.2 事务缓存管理

TransactionalCacheManager管理事务级别的缓存操作:
  1. package org.apache.ibatis.executor;
  2. public class TransactionalCacheManager {
  3.     private final Map<Cache, TransactionalCache> transactionalCaches = new HashMap<>();
  4.   
  5.     public void clear(Cache cache) {
  6.         getTransactionalCache(cache).clear();
  7.     }
  8.   
  9.     public Object getObject(Cache cache, CacheKey key) {
  10.         return getTransactionalCache(cache).getObject(key);
  11.     }
  12.   
  13.     public void putObject(Cache cache, CacheKey key, Object value) {
  14.         getTransactionalCache(cache).putObject(key, value);
  15.     }
  16.   
  17.     public void commit() {
  18.         for (TransactionalCache txCache : transactionalCaches.values()) {
  19.             txCache.commit();
  20.         }
  21.     }
  22.   
  23.     public void rollback() {
  24.         for (TransactionalCache txCache : transactionalCaches.values()) {
  25.             txCache.rollback();
  26.         }
  27.     }
  28.   
  29.     private TransactionalCache getTransactionalCache(Cache cache) {
  30.         TransactionalCache txCache = transactionalCaches.get(cache);
  31.         if (txCache == null) {
  32.             txCache = new TransactionalCache(cache);
  33.             transactionalCaches.put(cache, txCache);
  34.         }
  35.         return txCache;
  36.     }
  37. }
复制代码
8. 执行器选择策略

8.1 默认选择逻辑

MyBatis通过Configuration的newExecutor方法创建执行器:
  1. public Executor newExecutor(Transaction transaction, ExecutorType execType) {
  2.     execType = execType == null ? defaultExecutorType : execType;
  3.     execType = execType == null ? ExecutorType.SIMPLE : execType;
  4.     Executor executor;
  5.     if (ExecutorType.BATCH == execType) {
  6.         executor = new BatchExecutor(this, transaction);
  7.     } else if (ExecutorType.REUSE == execType) {
  8.         executor = new ReuseExecutor(this, transaction);
  9.     } else {
  10.         executor = new SimpleExecutor(this, transaction);
  11.     }
  12.     if (cacheEnabled) {
  13.         executor = new CachingExecutor(executor);
  14.     }
  15.     executor = (Executor) interceptorChain.pluginAll(executor);
  16.     return executor;
  17. }
复制代码
8.2 选择建议

场景推荐执行器原因一般业务查询SimpleExecutor简单可靠,适合大多数场景重复SQL查询ReuseExecutor减少Statement创建开销批量数据操作BatchExecutor大幅提升批量操作性能需要缓存CachingExecutor + 任意基础执行器提供二级缓存支持高并发读多CachingExecutor + ReuseExecutor结合缓存和Statement重用9. 实践案例:自定义执行器

9.1 性能监控执行器

让我们创建一个性能监控执行器,记录SQL执行时间:
  1. package com.example.executor;
  2. import org.apache.ibatis.executor.Executor;
  3. import org.apache.ibatis.executor.ExecutorException;
  4. import org.apache.ibatis.mapping.BoundSql;
  5. import org.apache.ibatis.mapping.MappedStatement;
  6. import org.apache.ibatis.plugin.Interceptor;
  7. import org.apache.ibatis.plugin.Intercepts;
  8. import org.apache.ibatis.plugin.Invocation;
  9. import org.apache.ibatis.plugin.Plugin;
  10. import org.apache.ibatis.plugin.Signature;
  11. import org.apache.ibatis.session.ResultHandler;
  12. import org.apache.ibatis.session.RowBounds;
  13. import java.util.Properties;
  14. @Intercepts({
  15.     @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
  16.     @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
  17. })
  18. public class PerformanceMonitorInterceptor implements Interceptor {
  19.   
  20.     private long slowQueryThreshold = 1000; // 慢查询阈值,默认1秒
  21.   
  22.     @Override
  23.     public Object intercept(Invocation invocation) throws Throwable {
  24.         long startTime = System.currentTimeMillis();
  25.         Object result = invocation.proceed();
  26.         long endTime = System.currentTimeMillis();
  27.         long executionTime = endTime - startTime;
  28.       
  29.         MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
  30.         String methodName = invocation.getMethod().getName();
  31.       
  32.         if (executionTime > slowQueryThreshold) {
  33.             System.out.println(String.format("SLOW QUERY DETECTED: %s.%s executed in %d ms",
  34.                 ms.getId(), methodName, executionTime));
  35.         }
  36.       
  37.         System.out.println(String.format("SQL Execution: %s.%s completed in %d ms",
  38.             ms.getId(), methodName, executionTime));
  39.       
  40.         return result;
  41.     }
  42.   
  43.     @Override
  44.     public Object plugin(Object target) {
  45.         return Plugin.wrap(target, this);
  46.     }
  47.   
  48.     @Override
  49.     public void setProperties(Properties properties) {
  50.         String threshold = properties.getProperty("slowQueryThreshold");
  51.         if (threshold != null) {
  52.             this.slowQueryThreshold = Long.parseLong(threshold);
  53.         }
  54.     }
  55. }
复制代码
9.2 配置使用

在mybatis-config.xml中配置插件:
  1. <configuration>
  2.     <plugins>
  3.         <plugin interceptor="com.example.executor.PerformanceMonitorInterceptor">
  4.             <property name="slowQueryThreshold" value="500"/>
  5.         </plugin>
  6.     </plugins>
  7.   
  8.     <settings>
  9.         <setting name="defaultExecutorType" value="REUSE"/>
  10.         <setting name="cacheEnabled" value="true"/>
  11.     </settings>
  12. </configuration>
复制代码
9.3 测试代码

[code]package com.example;import org.apache.ibatis.io.Resources;import org.apache.ibatis.session.SqlSession;import org.apache.ibatis.session.SqlSessionFactory;import org.apache.ibatis.session.SqlSessionFactoryBuilder;import org.apache.ibatis.session.ExecutorType;import java.io.InputStream;/** * Executor执行器示例测试类 * 展示不同类型执行器的使用和性能特点 */public class ExecutorExample {    public static void main(String[] args) throws Exception {        // 加载MyBatis配置文件        String resource = "mybatis-config.xml";        InputStream inputStream = Resources.getResourceAsStream(resource);        SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);              // 测试不同类型的执行器        testSimpleExecutor(factory);        testReuseExecutor(factory);        testBatchExecutor(factory);    }      /**     * 测试SimpleExecutor(简单执行器)     * 特点:每次执行都创建新的Statement     */    private static void testSimpleExecutor(SqlSessionFactory factory) {        System.out.println("=== 测试 SimpleExecutor ===");              // 使用SIMPLE类型执行器创建SqlSession        try (SqlSession session = factory.openSession(ExecutorType.SIMPLE)) {            UserMapper mapper = session.getMapper(UserMapper.class);                      // 执行多次相同查询,观察性能表现            for (int i = 0; i < 3; i++) {                System.out.println(">>> 第" + (i + 1) + "次查询(SimpleExecutor)");                User user = mapper.findById(1L);                System.out.println("查询结果: " + user);            }                      System.out.println("注意:每次查询都会创建新的Statement,但会命中一级缓存");        }    }      /**     * 测试ReuseExecutor(重用执行器)     * 特点:相同SQL会重用Statement对象     */    private static void testReuseExecutor(SqlSessionFactory factory) {        System.out.println("=== 测试 ReuseExecutor ===");              // 使用REUSE类型执行器创建SqlSession        try (SqlSession session = factory.openSession(ExecutorType.REUSE)) {            UserMapper mapper = session.getMapper(UserMapper.class);                      // 执行多次相同查询,Statement会被重用            for (int i = 0; i < 3; i++) {                System.out.println(">>> 第" + (i + 1) + "次查询(ReuseExecutor)");                User user = mapper.findById(1L);                System.out.println("查询结果: " + user);            }                      System.out.println("注意:相同SQL会重用Statement,性能优于 SimpleExecutor");                      // 测试不同SQL的情况            System.out.println(">>> 执行不同SQL");            User user2 = mapper.findByName("John");            System.out.println("不同SQL查询结果: " + user2);        }    }      /**     * 测试BatchExecutor(批量执行器)     * 特点:将多个操作打包成批量执行,提升性能     */    private static void testBatchExecutor(SqlSessionFactory factory) {        System.out.println("=== 测试 BatchExecutor ===");              // 使用BATCH类型执行器创建SqlSession        try (SqlSession session = factory.openSession(ExecutorType.BATCH)) {            UserMapper mapper = session.getMapper(UserMapper.class);                      System.out.println(">>> 执行批量插入操作");                      // 批量插入多个用户            for (int i = 1; i
您需要登录后才可以回帖 登录 | 立即注册