概述
在前边几篇的讲解中,我们应该已经对Executor执行器很熟悉了。Executor是MyBatis的SQL执行器,MyBatis采用了外观模式,SqlSession是面向程序的,提供了供上层调用的各类增删改查接口,而真正执行的是Executor,Executor是面向数据库的。本篇将对Executor相关的源码做解析。
从Executor接口说起
package org.apache.ibatis.executor;
//该接口里提供了update、query、commit、clearLocalCache等方法
public interface Executor {
ResultHandler NO_RESULT_HANDLER = null;
int update(MappedStatement var1, Object var2) throws SQLException;
<E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4, CacheKey var5, BoundSql var6) throws SQLException;
<E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4) throws SQLException;
<E> Cursor<E> queryCursor(MappedStatement var1, Object var2, RowBounds var3) throws SQLException;
List<BatchResult> flushStatements() throws SQLException;
void commit(boolean var1) throws SQLException;
void rollback(boolean var1) throws SQLException;
CacheKey createCacheKey(MappedStatement var1, Object var2, RowBounds var3, BoundSql var4);
boolean isCached(MappedStatement var1, CacheKey var2);
void clearLocalCache();
void deferLoad(MappedStatement var1, MetaObject var2, String var3, CacheKey var4, Class<?> var5);
Transaction getTransaction();
void close(boolean var1);
boolean isClosed();
void setExecutorWrapper(Executor var1);
}
Executor接口共有两个实现类,分别是BaseExecutor和CachingExecutor,这里先讲BaseExecutor
先讲BaseExecutor
BaseExecutor看名称可以知道它是一个基类,它有四个子类SimpleExecutor、ReuseExecutor、BatchExecutor以及ClosedExecutor(我们只需关注前三个,impleExecutor只是简单执行sql、ReuseExecutor会重用statement执行sql操作、BatchExecutor专门用于执行批量sql操作;分别对应ExecutorType.SIMPLE、ExecutorType.REUSE、ExecutorType.BATCH)
查看BaseExecutor子类BaseExecutor的成员属性如下:
protected Transaction transaction;//事务
protected Executor wrapper;//执行器包装者
protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads; //线程安全队列
// 查询操作的结果缓存
protected PerpetualCache localCache;
// Callable查询的输出参数缓存
protected PerpetualCache localOutputParameterCache;
protected Configuration configuration;
protected int queryStack; //查询次数栈
private boolean closed; //是否已关闭(回滚的时候会被关闭)
BaseExecutor获取数据库连接对象:
/**
* 获取一个Connection对象
* @param statementLog 日志对象
* @return Connection对象
* @throws SQLException
*/
protected Connection getConnection(Log statementLog) throws SQLException {
Connection connection = transaction.getConnection();
if (statementLog.isDebugEnabled()) { // 启用调试日志
// 生成Connection对象的具有日志记录功能的代理对象ConnectionLogger对象
return ConnectionLogger.newInstance(connection, statementLog, queryStack);
} else {
// 返回原始的Connection对象
return connection;
}
}
BaseExecutor执行query方法的源码如下:
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);
}
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) {
// 本地缓存中有结果,则对于CALLABLE语句还需要绑定到IN/INOUT参数上
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();
// 如果本地缓存的作用域为STATEMENT,则立刻清除本地缓存
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
clearLocalCache();
}
}
return list;
}
主要看第二个query方法,BaseExecutor会优先从缓存中查询数据,如果缓存不为空再从数据库数据。
如果没有缓存的话,BaseExecutor会调用queryFromDatabase方法从数据库查询数据:
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;
}
这个方法中调用了doQuery方法从数据库查询了数据,然后将查询的结果及查询用的参数放入了缓存中,而doQuery方法在BaseExecutor中是抽象方法,具体的实现是由BaseExecutor的子类进行实现,可以看成是模板模式
protected abstract int doUpdate(MappedStatement ms, Object parameter)
throws SQLException;
protected abstract List<BatchResult> doFlushStatements(boolean isRollback)
throws SQLException;
protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException;
protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)
throws SQLException;
查看一下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 handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
同理doUpdate、doFlushStatements、doQueryCursor方法,大家可以按照这个思路去一个个点看查看,也都在各个子类里有自己不同的实现。
接着讲CachingExecutor
CachingExecutor从名字看,它是跟缓存有关,没错,MyBatis的二级缓存是在这个类里边实现的。
CachingExecutor的成员属性和构造函数源码如下:
// 被装饰的执行器
private final Executor delegate;
// 事务缓存管理器
private final TransactionalCacheManager tcm = new TransactionalCacheManager();
public CachingExecutor(Executor delegate) {
this.delegate = delegate;
delegate.setExecutorWrapper(this);
}
CachingExecutor有一个重要属性delegate,它保存的是BaseExecutor的子类的对象,在构造时传入。CachingExecutor只是起了一个代理的作用,执行数据库update操作时,它直接调用delegate的update方法,执行query方法时先尝试从cache中取值,取不到再调用delegate的query方法,并将查询结果存入cache中。
这里就查看一下CachingExecutor中的update方法和query方法源码,代码上加了注释:
/**
* 更新数据库数据,INSERT/UPDATE/DELETE三种操作都会调用该方法
* @param ms 映射语句
* @param parameterObject 参数对象
* @return 数据库操作结果
* @throws SQLException
*/
@Override
public int update(MappedStatement ms, Object parameterObject) throws SQLException {
// 根据要求判断语句执行前是否要清除二级缓存,如果需要,清除二级缓存
flushCacheIfRequired(ms);
return delegate.update(ms, parameterObject);
}
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);
}
/**
* 查询数据库中的数据
* @param ms 映射语句
* @param parameterObject 参数对象
* @param rowBounds 翻页限制条件
* @param resultHandler 结果处理器
* @param key 缓存的键
* @param boundSql 查询语句
* @param <E> 结果类型
* @return 结果列表
* @throws SQLException
*/
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
// 获取MappedStatement对应的缓存,可能的结果有:该命名空间的缓存、共享的其它命名空间的缓存、无缓存
Cache cache = ms.getCache();
// 如果映射文件未设置<cache>或<cache-ref>则,此处cache变量为null
if (cache != null) { // 存在缓存
// 根据要求判断语句执行前是否要清除二级缓存,如果需要,清除二级缓存
flushCacheIfRequired(ms);
if (ms.isUseCache() && resultHandler == null) { // 该语句使用缓存且没有输出结果处理器
// 二级缓存不支持含有输出参数的CALLABLE语句,故在这里进行判断
ensureNoOutParams(ms, boundSql);
// 从缓存中读取结果
@SuppressWarnings("unchecked")
List<E> list = (List<E>) tcm.getObject(cache, key);
if (list == null) { // 缓存中没有结果
// 交给被包装的执行器执行
list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
// 缓存被包装执行器返回的结果
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
// 交由被包装的实际执行器执行
return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
同理其他的方法以此类推,可以一一查看一下。
总结
Executor执行器应该是MyBatis最核心的部分了,熟悉源码 借鉴里边的设计模式,多思考,可以提高自己的代码以及系统设计能力。
网友评论