Executor的query和doQuery
sorry,好久没更新了,今天有时间,就多写点吧!
上次讲到,在DefaultSqlSession中的selectList方法,调用了executor的query方法。但这个query的方法具体是怎么实现的呢?这节来分析这个。
首先我看来看看这个executor究竟是那个实现类。这就要追溯到创建executor的地方了。
位置在
org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSessionFromDataSource
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
进入到configuration中的newExecutor方法
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
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;
}
从中可以看到,由于我们用的是默认的executorType,所以应该创建的是SimpleExecutor,并且我们没有关闭cacheEnable,所以会在executor外面包一层CachingExecutor.
public CachingExecutor(Executor delegate) {
this.delegate = delegate;
delegate.setExecutorWrapper(this);
}
这是CachingExecutor的构造方法。由此可见delegate对象就是SimpleExecutor(在这个场景而言,其他情况为相对应的Executor)
那我们再回过头来看executor的query方法。
这里的query实现方法应该是cachingExecutor的query方法。
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameterObject);
CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
第一个参数ms上一节有提到,是所要执行sql的各项配置环境。第二个参数是执行sql的参数,第三个是selectList是的偏移量和大小的设置。最后一个是对返回结果的处理方法。
看这个方法的逻辑。从ms中拿出BoundSql,boundSql中包括了sql的内容(select XX from XXX where XX=?之类的)已经sql的所需参数的信息。
再通过这些参数创建一个缓存的key,然后根据这些属性再继续调用包含key的query方法。
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, parameterObject, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578. Query must be not synchronized to prevent deadlocks
}
return list;
}
}
return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
这个方法也比较简单,先去取缓存,取得到就去缓存拿,取不到就调用真正的执行器去执行sql。(这里的缓存是二级缓存,暂且不讲)
看到这个delegate的query方法就知道去执行了SimpleExecutor里的query方法了。但实际上SimpleExecutor是没有query方法的,这里执行的query方法是其父类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.");
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();
}
deferredLoads.clear(); // issue #601
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
clearLocalCache(); // issue #482
}
}
return list;
}
看到这个方法又去拿了一次缓存,其实这里是一级缓存,也暂且不讲。这里的真正实现是queryFromDatabase的doQuery方法,而这个doquery方法,就是SimpleExecutor里的方法了。
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);
}
}
这里拿到Configuration然后再创建statement,statement是什么,参考第一节的jdbc连接数据库方法。(是不是感觉接近真相了)
然后执行handler的query方法。
这个方法具体又是怎么操作的呢?下节分析。