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 执行器继承关系图
delegates to
«interface»
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
«abstract»
BaseExecutor
#localCache PerpetualCache
#localOutputParameterCache PerpetualCache
#deferredLoads List<DeferredLoad>
#queryStack int
#closed boolean
#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>
SimpleExecutor
+doQuery(MappedStatement, Object, RowBounds, ResultHandler, BoundSql) : List<E>
+doUpdate(MappedStatement, Object) : int
+doFlushStatements(boolean) : List<BatchResult>
+doQueryCursor(MappedStatement, Object, RowBounds, BoundSql) : Cursor<E>
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
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
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
2.2 执行器职责分工
执行器类型 核心职责 适用场景 性能特点
BaseExecutor 模板方法实现,一级缓存管理 所有场景的基础 提供通用功能和缓存
SimpleExecutor 简单执行,每次创建新Statement 单次执行、简单查询 简单直接,资源及时释放
ReuseExecutor 重用Statement对象 重复执行相同SQL 减少Statement创建开销
BatchExecutor 批量执行多个SQL 批量插入、更新、删除 大幅减少数据库交互次数
CachingExecutor 二级缓存管理 需要缓存的查询场景 避免重复查询,提升性能
2.3 执行器协作关系
Database
ResultSetHandler
ParameterHandler
StatementHandler
Executor
SqlSession
Database
ResultSetHandler
ParameterHandler
StatementHandler
Executor
SqlSession
query(ms, param, rowBounds, handler)
createCacheKey(ms, param, rowBounds, boundSql)
queryFromDatabase(ms, param, rowBounds, handler, key, boundSql)
newStatementHandler(wrapper, ms, param, rowBounds, handler, boundSql)
prepare(connection, timeout)
parameterize(statement)
set parameters
execute query
ResultSet
handleResultSets(statement)
List<Object>
results
List<E>
3. BaseExecutor抽象基类深度解析
3.1 模板方法模式实现
BaseExecutor采用模板方法模式,定义了SQL执行的标准流程,子类只需实现具体的执行逻辑:
package org.apache.ibatis.executor;
/**
* 执行器抽象基类,采用模板方法模式
* 定义SQL执行的标准流程,子类实现具体的执行逻辑
*/
public abstract class BaseExecutor implements Executor {
// 事务管理器,用于管理数据库事务
protected Transaction transaction;
// 执行器包装器,通常是CachingExecutor
protected Executor wrapper;
// 一级缓存(本地缓存),session级别缓存
protected PerpetualCache localCache;
// 存储过程出参缓存
protected PerpetualCache localOutputParameterCache;
// MyBatis全局配置对象
protected Configuration configuration;
// 查询栈深度,用于控制嵌套查询
protected int queryStack;
// 执行器是否已关闭
private boolean closed;
// 延迟加载队列,存储需要延迟加载的对象
protected List<DeferredLoad> deferredLoads = new ArrayList<>();
/**
* 模板方法:查询操作的标准流程(重载方法1)
* @param ms SQL映射语句
* @param parameter 查询参数
* @param rowBounds 分页参数
* @param resultHandler 结果处理器
* @return 查询结果列表
*/
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
// 根据参数生成动态SQL
BoundSql boundSql = ms.getBoundSql(parameter);
// 创建缓存键,用于一级缓存
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
// 调用重载方法继续执行
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
/**
* 模板方法:查询操作的标准流程(重载方法2)
* 这是查询的核心方法,包含完整的缓存和执行逻辑
*/
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException {
// 设置错误上下文,便于调试和错误定位
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
// 检查执行器是否已关闭
if (closed) {
throw new ExecutorException("Executor was closed.");
}
// 如果是最外层查询且配置了刷新缓存,则清空本地缓存
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
// 查询栈深度+1,用于处理嵌套查询
queryStack++;
// 尝试从一级缓存中获取结果(仅当没有结果处理器时)
list = resultHandler == null ? (List<E>) localCache.getObject(cacheKey) : null;
if (list != null) {
// 缓存命中,处理存储过程的输出参数
handleLocallyCachedOutputParameters(ms, cacheKey, parameter, boundSql);
} else {
// 缓存未命中,从数据库查询
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
}
} finally {
// 查询栈深度-1
queryStack--;
}
// 如果回到最外层查询
if (queryStack == 0) {
// 执行所有延迟加载
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// 清空延迟加载队列
deferredLoads.clear();
// 如果配置为STATEMENT级别缓存,执行完成后清空缓存
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
clearLocalCache();
}
}
return list;
}
/**
* 抽象方法:子类实现具体的数据库查询逻辑
* 不同的执行器有不同的实现策略
*/
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException;
/**
* 模板方法:更新操作的标准流程
* 包括插入、更新、删除操作
*/
@Override
public int update(MappedStatement ms, Object parameter) throws SQLException {
// 设置错误上下文
ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
// 检查执行器是否已关闭
if (closed) {
throw new ExecutorException("Executor was closed.");
}
// 清空本地缓存(更新操作会影响缓存一致性)
clearLocalCache();
// 调用子类的具体实现
return doUpdate(ms, parameter);
}
/**
* 抽象方法:子类实现具体的数据库更新逻辑
*/
protected abstract int doUpdate(MappedStatement ms, Object parameter) throws SQLException;
}
3.2 一级缓存机制
BaseExecutor实现了一级缓存(本地缓存),提升重复查询的性能:
/**
* 从数据库查询数据并缓存结果
* 这是一级缓存的核心实现方法
*/
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException {
List<E> list;
// 在缓存中放入占位符,防止循环引用
localCache.putObject(cacheKey, EXECUTION_PLACEHOLDER);
try {
// 调用子类的具体实现执行数据库查询
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
// 无论成功还是失败,都要移除占位符
localCache.removeObject(cacheKey);
}
// 将查询结果放入一级缓存
localCache.putObject(cacheKey, list);
// 如果是存储过程调用,需要缓存输出参数
if (ms.getStatementType() == StatementType.CALLABLE) {
localOutputParameterCache.putObject(cacheKey, parameter);
}
return list;
}
/**
* 创建缓存键
* 缓存键由多个因素组成,确保唯一性
*/
@Override
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
// 检查执行器是否已关闭
if (closed) {
throw new ExecutorException("Executor was closed.");
}
// 创建缓存键对象
CacheKey cacheKey = new CacheKey();
// 添加SQL映射语句ID到缓存键中
cacheKey.update(ms.getId());
// 添加分页参数到缓存键中
cacheKey.update(rowBounds.getOffset());
cacheKey.update(rowBounds.getLimit());
// 添加SQL语句到缓存键中
cacheKey.update(boundSql.getSql());
// 获取参数映射列表
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
// 遍历所有参数,将参数值添加到缓存键中
for (ParameterMapping parameterMapping : parameterMappings) {
// 只处理输入参数,不处理输出参数
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
// 获取参数值的逐个判断逻辑
if (boundSql.hasAdditionalParameter(propertyName)) {
// 从额外参数中获取
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
// 参数对象为空
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
// 参数对象有对应的类型处理器(基本类型)
value = parameterObject;
} else {
// 复杂对象,通过反射获取属性值
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
// 将参数值添加到缓存键中
cacheKey.update(value);
}
}
// 将环境ID添加到缓存键中(区分不同环境)
if (configuration.getEnvironment() != null) {
cacheKey.update(configuration.getEnvironment().getId());
}
return cacheKey;
}
3.3 延迟加载机制
BaseExecutor支持延迟加载(懒加载),通过DeferredLoad实现:
/**
* 延迟加载类,用于实现懒加载功能
* 当访问某个属性时,才真正执行相关查询
*/
public class DeferredLoad {
// 结果对象的元数据包装器,用于反射操作
private final MetaObject resultObject;
// 需要延迟加载的属性名
private final String property;
// 目标类型(属性的类型)
private final Class<?> targetType;
// 缓存键,用于标识该次查询
private final CacheKey key;
// SQL映射语句,包含延迟加载的SQL信息
private final MappedStatement mappedStatement;
// 执行器,用于执行延迟加载查询
private final Executor executor;
/**
* 构造延迟加载对象
*/
public DeferredLoad(MetaObject resultObject, String property, Class<?> targetType, CacheKey key, MappedStatement mappedStatement, Executor executor) {
this.resultObject = resultObject;
this.property = property;
this.targetType = targetType;
this.key = key;
this.mappedStatement = mappedStatement;
this.executor = executor;
}
/**
* 执行延迟加载
* 这个方法会在适当的时机被调用,执行实际的数据库查询
*/
public void load() throws SQLException {
// 检查属性是否已经有值,如果已经有值则不需要加载
if (resultObject.getValue(property) != null) {
return;
}
// 执行实际的数据库查询
List<Object> list = (List<Object>) executor.query(
mappedStatement,
key.getParameterObject(),
RowBounds.DEFAULT,
Executor.NO_RESULT_HANDLER,
key,
mappedStatement.getBoundSql(key.getParameterObject())
);
// 处理查询结果
if (list != null && list.size() > 0) {
if (list.size() > 1) {
// 多个结果,设置为列表
resultObject.setValue(property, list);
} else {
// 单个结果,设置为单个对象
resultObject.setValue(property, list.get(0));
}
}
}
}
4. SimpleExecutor简单执行器
4.1 核心实现
SimpleExecutor是最基础的执行器,每次执行都创建新的Statement:
package org.apache.ibatis.executor;
/**
* 简单执行器:MyBatis的默认执行器
* 特点:每次执行都创建新的Statement对象
* 优点:简单可靠,资源管理清晰
* 缺点:每次都要创建新Statement,有一定性能开销
*/
public class SimpleExecutor extends BaseExecutor {
/**
* 构造方法
* @param configuration MyBatis全局配置
* @param transaction 事务管理器
*/
public SimpleExecutor(Configuration configuration, Transaction transaction) {
super(configuration, transaction);
}
/**
* 执行查询操作
* 每次查询都会创建新的Statement对象
*/
@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,用于处理 SQL 语句
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// 准备 Statement(创建并设置参数)
stmt = prepareStatement(handler, ms.getStatementLog());
// 执行查询并返回结果
return handler.query(stmt, resultHandler);
} finally {
// 无论成功还是失败,都要关闭 Statement
closeStatement(stmt);
}
}
/**
* 执行更新操作(包括 INSERT、UPDATE、DELETE)
*/
@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
try {
// 获取全局配置
Configuration configuration = ms.getConfiguration();
// 创建 StatementHandler
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
// 准备 Statement
stmt = prepareStatement(handler, ms.getStatementLog());
// 执行更新并返回影响行数
return handler.update(stmt);
} finally {
// 关闭 Statement
closeStatement(stmt);
}
}
/**
* 刷新批量操作
* SimpleExecutor 不支持批量操作,返回空列表
*/
@Override
public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
return Collections.emptyList();
}
/**
* 执行游标查询
* 游标查询适用于大结果集的流式处理
*/
@Override
public <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException {
// 获取全局配置
Configuration configuration = ms.getConfiguration();
// 创建 StatementHandler
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, null, boundSql);
// 准备 Statement
Statement stmt = prepareStatement(handler, ms.getStatementLog());
// 返回游标对象(注意:游标不在这里关闭,由调用者负责关闭)
return handler.queryCursor(stmt);
}
/**
* 准备 Statement对象
* 包括创建、设置超时、设置参数等操作
*/
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
// 获取数据库连接
Connection connection = getConnection(statementLog);
// 创建 Statement 并设置超时时间
stmt = handler.prepare(connection, transaction.getTimeout());
// 设置 SQL 参数
handler.parameterize(stmt);
return stmt;
}
/**
* 关闭 Statement对象
* 安全关闭,忽略异常
*/
private void closeStatement(Statement stmt) {
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
// 忽略关闭异常,避免影响主逻辑
}
}
}
}
4.2 特点分析
优势:
简单可靠:逻辑简单,易于理解和维护
资源管理:及时释放Statement资源,避免内存泄漏
线程安全:每次执行创建新Statement,无状态共享问题
适用广泛:适合大多数业务场景
劣势:
性能开销:每次执行都创建新Statement,有一定性能开销
重复工作:相同SQL的重复执行无法复用Statement
5. ReuseExecutor重用执行器
5.1 核心实现
ReuseExecutor通过重用Statement对象来提升性能:
package org.apache.ibatis.executor;
/**
* 重用执行器:通过重用Statement对象来提升性能
* 特点:相同SQL会重用同一个Statement对象
* 优点:减少Statement创建开销,提升性能
* 适用场景:重复执行相同SQL的场景
*/
public class ReuseExecutor extends BaseExecutor {
// Statement缓存Map,以SQL为键,Statement为值
private final Map<String, Statement> statementMap = new HashMap<>();
/**
* 构造方法
*/
public ReuseExecutor(Configuration configuration, Transaction transaction) {
super(configuration, transaction);
}
/**
* 执行查询操作
* 会尝试重用已存在的Statement
*/
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// 准备Statement(可能会重用已存在的)
Statement stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
}