1 概述
Mybatis中所有的Mapper语句的执行都是通过Executor进行的,Executor是Mybatis的一个核心接口。针对Executor的学习,我们先来说说Executor的生成和Executor的分类,然后再来看看其中某个典型方法的具体执行。
2 Executor生成
Executor是通过Configuration的newExecutor函数来生成的,我们来看一看newExecutor的逻辑。
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
//获取执行器类型,如果没有就使用SIMPLE
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor 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);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
//插件处理执行器
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
从上面的源码我们可以看出这里针对不同的执行器类型来生成不同的执行器,针对插件处理执行器,这里使用到了责任链模式,后面在分析插件的时候在具体谈。
2 Executor 结构
从上面的Executor的创建我们猜到了Executor仅仅是Mybatis的执行器接口,这里针对不同的执行器类型会创建不同的执行器对象。我们来看一看执行器类型枚举类。
public enum ExecutorType {
SIMPLE, REUSE, BATCH
}
由上面的执行器类型枚举类,我们直到了Mybatis共有三种执行器。分别的作用如下:
SimpleExecutor -- 执行mapper语句的时候,默认的Executor。
ReuseExecutor -- 针对相同的sql可以重用Statement。
BatchExecutor --用于批量操作。
我们来看一下执行器的类图:
3 DefaultSqlSession的selectList函数
在这里为了对Executor有一个深入认识,我们先看一看从DefaultSqlSession的selectList函数到Eexcutor的doQuery函数的整个执行流程。
首先来看一下selectList函数的内部实现。
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
这里先获得Mapper文件中执行节点得MappedStatement封装,然后调用Executor的query函数。
这里的query函数来自Executor的子类BaseExecutor。
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
// 利用参数parameter来解析得到BoundSql(BoundSql其实就是对mapper中的执行语句块进行进一步抽象)
BoundSql boundSql = ms.getBoundSql(parameter);
//创建缓存key
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
//执行查询并返回
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
针对上面的内容,我们来看一下创建缓存key的逻辑。
//创建缓存key
@Override
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
if (closed) {
throw new ExecutorException("Executor was closed.");
}
//生成CacheKey对象,这个对象其实就是对一些sql内容的缓存
CacheKey cacheKey = new CacheKey();
cacheKey.update(ms.getId());
cacheKey.update(Integer.valueOf(rowBounds.getOffset()));
cacheKey.update(Integer.valueOf(rowBounds.getLimit()));
cacheKey.update(boundSql.getSql());
//获取mapper节点映射的参数
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
// mimic DefaultParameterHandler logic
for (int i = 0; i < parameterMappings.size(); i++) {
ParameterMapping parameterMapping = parameterMappings.get(i);
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);
}
}
if (configuration.getEnvironment() != null) {
//缓存环境对象Id
cacheKey.update(configuration.getEnvironment().getId());
}
return cacheKey;
}
接下来调用BaseExecutor的另一个重载query函数。
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
//如果没有执行过查询并且mapper语句块要求刷新缓存这里就清空本地缓存
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;
//如果结果处理器为空则直接从缓存种获取
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
//处理存储过程的out参数
if (list != null) {
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
//如果缓存种没有数据则从数据库获取
} else {
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
queryStack--;
}
if (queryStack == 0) {
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
这里如果没有缓存数据,最终会调用queryFromDatabase函数去获取数据。
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
//针对缓存key设置缓存占位符
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
//调用doQuery函数,执行数据库结果查询,这里其实就有点类似于模板方法模式了
list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql);
} finally {
localCache.removeObject(key);
}
//缓存查询结果
localCache.putObject(key, list);
if (ms.getStatementType() == StatementType.CALLABLE) {
//缓存存储过程输出参数
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
其实我们可以看见,BaseExecutor处理了事务、存储过程输出参数、缓存和创建连接这些。具体的查询实现就留给了它的子类来实现。
4 Executor的具体函数
接着上面我们可以发现BaseExecutor处理了事务、存储过程输出参数、缓存和创建连接这些,针对具体的查询的执行就留给了它的子类,这里就有点类似于模板方法设计模式。
这里针对上面提到的doQuery函数,我们就来看一下SimpleExecutor类下的函数。
public class SimpleExecutor extends BaseExecutor {
public SimpleExecutor(Configuration configuration, Transaction transaction) {
super(configuration, transaction);
}
@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
try {
//获取配置对象
Configuration configuration = ms.getConfiguration();
//获取StatementHandler对象,这里的StatementHandler对象其实就是对JDBC里面sql的执行进行的封装,我们后面将详细分析。
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
//准备statement
stmt = prepareStatement(handler, ms.getStatementLog());
//执行并返回结果
return handler.update(stmt);
} finally {
closeStatement(stmt);
}
}
@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 handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
@Override
public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
//刷新缓存
return Collections.emptyList();
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
//建立连接
Connection connection = getConnection(statementLog);
//准备工作包括创建statement和对连接超时时间的设置
stmt = handler.prepare(connection);
//参数预处理,这里仅仅时PreparedStatementHandler有参数预处理实现
handler.parameterize(stmt);
return stmt;
}
}
从上面我们可以看出来,针对statement的准备工作就交给了StatementHandler来完成,最终通过handler调用具体的CRUD方法获取执行结果。总结起来Eexcutor其实就是做好数据库交互前面的准备工作,包括缓存、事务、连接、statement等。而具体的Executor其实就是获取到相应的StatementHandler,创建好连接,然后执行StatemetnHandler对应的函数。而具体的和数据库的操作就交给了具体的StatementHandler。
接下来我们再来分析下StatementHandler的源码。欢迎交流。