「这是我参与11月更文挑战的第9天,活动详情查看:2021最后一次更文挑战」
概述
到目前为止,已经知道了:
- executor负责的部分,是具体执行SQL并返回结果这一工作的。
实际上,executor的连接信息被封装在了内部的Transaction对象中。
我们知道,myBatis是有内置缓存这一说的,在Executor中亦有这一说法:
- 需要注意的是,缓存一般我们是对应查询的,因此只在查询中有用到。
因此,这里我们就专注于查询这一功能以及相关的了解。
在开始代码之前,按照源码解读的顺序先看看executor的继承关系:
-
大概的执行逻辑都被封装到了BaseExecutor中,而具体到每一项的执行逻辑,都用以do开头的方法,作为钩子提供给了子类进行实现。
单独实现的子类包括:
- BatchExecutor
- ReuseExecutor
- SimpleExecutor
这里就以SimpleExecutor为例,简单分析以下query的流程。
查询
在BaseExecutor中,mybatis执行了缓存的处理,具体代码如下:
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.");
}
if (queryStack == 0 && ms.isFlushCacheRequired()) {
clearLocalCache();
}
List<E> list;
try {
queryStack++;
list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
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;
}
private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List<E> list;
localCache.putObject(key, EXECUTION_PLACEHOLDER);
try {
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;
}
这里需要注意到:
-
本地缓存是通过map来进行原生保存的,因此在当前一个服务动不动布个多台的情况下,不做扩展,如果要做实时的查询,实际上基本没法用。
-
这里有一个queryStack,实际上这也是针对缓存的一个相关操作,标识当前执行中的SQL有多少。只有在当前没有SQL执行的时候,才回去更新缓存。
关联的还有一个叫DeferredLoad的类,保存的是每次查询的相关信息,实际上这些只在CachingExecutor中使用。只有在cachingExecutor中,缓存才分了这么多类,其他的其实都只是做了key到结果的映射。
接下来来看看子类SimpleExecutor具体的doQuery:
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.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
这里就发现了一个有趣的事实:
- 实际上最后一步的执行,是分配到了statementHandler中来具体执行的,executor实际上是一个抽象的执行层,下面还有一个statementHandler。
看一下这一块,这里能决定最后执行的handler是哪一个:
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
private final StatementHandler delegate;
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
再看一下这个RoutingStatementHandler下面的东西都是通过这个delegate执行的,恍然大悟:用的还是Simple。
那接下来就到了SimpleExecutor中的query了:
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
String sql = boundSql.getSql();
statement.execute(sql);
return resultSetHandler.handleResultSets(statement);
}
在普通的JDBC中,我们的结果是从statement里获取的,这里没有变。
这里的resultSetHandler,实际上就是我们定义的resultMapping;在这里就能看到,SQL的查询结果是如何变成对象的了。
之前做过excel的导入导出相关工作,如果自己写不用框架的话也是做一对一的mapping,其实这里也是。