mybatis源码分析-selectOne-03
Executor.png上篇文章我们分析到了 CachingExecutor ,本文我们就来详细的分析一下 CachingExecutor 。为了方便阅读我们再来看一下 Executor 的类图
1.1 CachingExecutor
public class CachingExecutor implements Executor {
private final Executor delegate;
private final TransactionalCacheManager tcm = new TransactionalCacheManager();
// 委派模式 ,持有 具体干活的实现类,自己一定不会干活,
//其实也可以看成是 静态代理的特殊情况,就是代理对象什么也不干,活都让 被代理的对象干了
public CachingExecutor(Executor delegate) {
this.delegate = delegate;// delegate -> SimpleExecutor
delegate.setExecutorWrapper(this);
}
....
@Override
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);
}
.....
@Override
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, boundSql);
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) {
// 执行 查询的 是 具体的实现了,CachingExecutor 自己不会有操作数据库的逻辑
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
// 执行 查询的 是 具体的实现了,CachingExecutor 自己不会有操作数据库的逻辑
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
上面这段逻辑就是在调用 delegate 对象的 query() 方法,我们先不管 if 条件这些判断,避开对我们看主线代码的影响,都是 走的 下面这句话:
delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
那么现在所有的关键都在这句话了,通过前面两篇文章的分析,我们知道 delegate -> SimpleExecutor 。接下来我们要看具体的 数据库操作 就要去 SimpleExecutor 了。
1.2 SimpleExecutor
从上面的类图我们知道 SimpleExecutor -> BaseExecutor-> Executor 。
SimpleExecutor 继承了 BaseExecutor ,BaseExecutor 是对接口的抽象实现,这里又有模板设计模式。我们先来看下代码:
/**
* @author Clinton Begin
*/
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 handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
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.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
@Override
protected <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, null, boundSql);
Statement stmt = prepareStatement(handler, ms.getStatementLog());
stmt.closeOnCompletion();
return handler.queryCursor(stmt);
}
@Override
public List<BatchResult> doFlushStatements(boolean isRollback) {
return Collections.emptyList();
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
}
上面是 SimpleExecutor 对所有方法,我们发现并没有我们 关心的 query() 方法,从类的关系我们也能看出,query 方法应该是在 父类BaseExecutor 中,前面我们说到这里有 模式设计模式,我们就来分析下是怎么回事:先看 BaseExecutor
/**
* @author Clinton Begin
*/
public abstract class BaseExecutor implements Executor {
.....
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
@SuppressWarnings("unchecked")
@Override
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;
}
......
//抽象方法,子类实现
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException;
}
.....
上面省略了部分代码,只保留了关键 部分。doQuery() 是抽象 方法,我们在 SimpleExecutor 中看到了 doQuery() 方法的实现,
@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 负责查询,结果解析封装
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
上面就 是具体的 doQuery() 方法的实现,从这里我么可以看出 doQuery() 中的 StatementHandler 是完成最后查询和结果封装的接口。到这里我们 分析了 CachingExecutor 和 SimpleExecutor ,源码里面使用了 委派模式和 模板方法,如果你对模板方法有点晕,我们来解释一下,当你在调用 SimpleExecutor 的 query() 方法时,由于子类没有定义该方法,该方法是从父类继承过来的,所以 调用的是父类的query() 方法,而在 父类的query() 方法中 调用的是 抽象的doQuery() 方法,子类实现了 doQuery() 方法,所以doQuery() 方法在 SimpleExecutor.query() 时被回调了,文字解释有点苍白,可以自己写一个demo 就明白了,这也是抽象的很好体现。但是委派模式在这里到底有啥好处,目前我也还不能体会其奥义。后面会接着分析 StatementHandler 接口,看看它又干了些啥事。