一 Executor 接口
1. 接口方法
根据之前对 SqlSession 类的分析(Mybatis源码学习(二)——SqlSession-CSDN博客)可以发现 SqlSession 与数据库交互的方法都是通过 Executor 对象进行;因此首先对 Executor 接口进行分析,看其拥有什么方法;
第一步我们可以发现 Executor 接口拥有一个 ResultHandler 的常量,但其值却为 null,起作用之后再详述;现阶段还是主要聚焦于接口中的方法,其中与 Sql 执行直接相关的方法有 4 个,分别为 update(更新语句)、两个形参列表有一定差异的查询列表类型数据集的 query 以及一个用于获取结果集 Cursor 指针对象的 queryCursor 方法;
与数据事务操作相关的方法有 3 个,commit 方法用于提交事务、rollback 方法用于事务的回退、以及 getTransaction 方法获取事务对象;
与缓存相关的方法有用于生成缓存 key 键的 createCacheKey 方法、用于判断是否拥有指定缓存的 isCached 方法以及用于清空本地缓存的 clearLocalCache 方法;
其余方法为用于刷新 Statement 对象的 flushStatements、 setExecutorWrapper、用于关闭执行器的 close 方法以及用于判断当前执行器是否关闭的 isClosed 方法。
public interface Executor {
ResultHandler NO_RESULT_HANDLER = null;
int update(MappedStatement ms, Object parameter) throws SQLException;
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler,
CacheKey cacheKey, BoundSql boundSql) throws SQLException;
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler)
throws SQLException;
<E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
List<BatchResult> flushStatements() throws SQLException;
void commit(boolean required) throws SQLException;
void rollback(boolean required) throws SQLException;
CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
boolean isCached(MappedStatement ms, CacheKey key);
void clearLocalCache();
void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType);
Transaction getTransaction();
void close(boolean forceRollback);
boolean isClosed();
void setExecutorWrapper(Executor executor);
}
2. Excutor 对象的创建
mybatis 使用的 Executor 对象都是在 DefaultSqlSessionFactory 工厂类中进行创建,然后将其用于创建 SqlSession 对象,创建 Executor 对象的方法为 Configuration 对象的 newExecutor 方法;
newExecutor 方法首先根据调用方传入的 executorType 创建基础 Executor 对象,再未传入时使用 setting 标签 defaultExecutorType 子标签配置的默认执行器类型;BATCH 执行类型对应创建 BatchExecutor 对象、REUSE 执行类型对应创建 ReuseExecutor 对象、其余情况下一律创建 SimpleExecutor 对象;随后系统启用缓存时会使用 CachingExecutor 对象对创建的基础执行器进行包装;最后调用 InterceptorChain 插件链的 pluginAll 方法将所有能匹配插件安装到执行器上。
public class Configuration {
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : 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);
}
return (Executor) interceptorChain.pluginAll(executor);
}
}
二 BaseExecutor 类
BaseExecutor 类作为 BatchExecutor、ReuseExecutor 及 SimpleExecutor 类的共同父类,大部分的执行方法都是共用的,因此我们首先对 BaseExecutor 类的方法进行分析;
1. 构造方法
与一般的 mybatis 中的大多数类一样,BaseExecutor 类也只有一个构造函数,只有两个参数:配置对象参数 configuration 与事务对象参数 transaction;
在构造方法中首先分别将 configuration 与 transaction 参数赋值给 BaseExecutor 执行器中的 configuration 与 transaction 对应参数值,然后将 deferredLoads 属性初始化为 ConcurrentLinkedQueue 对象、localCache 本地缓存对象与 localOutputParameterCache 本地入参缓存初始化为 PerpetualCache 对象、closed 是否关闭属性初始化为 false 以及 wrapper 属性初始化为当前对象;
public abstract class BaseExecutor implements Executor {
private static final Log log = LogFactory.getLog(BaseExecutor.class);
protected Transaction transaction;
protected Executor wrapper;
protected ConcurrentLinkedQueue<DeferredLoad> deferredLoads;
protected PerpetualCache localCache;
protected PerpetualCache localOutputParameterCache;
protected Configuration configuration;
protected int queryStack;
private boolean closed;
protected BaseExecutor(Configuration configuration, Transaction transaction) {
this.transaction = transaction;
this.deferredLoads = new ConcurrentLinkedQueue<>();
this.localCache = new PerpetualCache("LocalCache");
this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
this.closed = false;
this.configuration = configuration;
this.wrapper = this;
}
}
2. sql 交互方法
2.1 query 查询方法
BaseExecutor 类中拥有两个 query 方法,他们之间差异在于形参列表,第一个 query 方法比第二个少了缓存 key 与 BoundSql 两个参数,其调用 MappedStatement 对象参数的 getBoundSql 方法获取 BoundSql 对象,调用 createCacheKey 方法创建缓存 key,然后调用第二个 query 方法查询结果集;
query 方法首先验证本执行器对象是否关闭,若关闭了直接抛出异常;随后根据 queryStack 属性是否为 0 以及 MappedStatement 是否需要刷新缓存的结果,调用 clearLocalCache 方法清空本地缓存,包含 localCache 与 localOutputParameterCache 参数;
接下来进入查询逻辑,在查询的过程中首先将 queryStack + 1,随后在 resultHandler 为空时尝试从 localCache 缓存中获取结果,若获取到结果调用 handleLocallyCachedOutputParameters 方法更新入参缓存,否则调用 queryFromDatabase 方法从数据库中查询结果,最后将 queryStack - 1;
最后如果当前查询为最外层查询,则首先遍历 deferredLoads 并调用其 load 方法,随后清空 deferredLoads,最后如果缓存作用范围在为 STATEMENT,则直接清空本地缓存。
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;
}
}
handleLocallyCachedOutputParameters 方法只有在 MappedStatement 对象为 CALLABLE 类型才会继续执行,随后尝试从 localOutputParameterCache 缓存中获取方法参数,只有在之前缓存的参数与当前参数都不为空时才会继续处理,接下来遍历 BoundSql 的 parameterMappings 属性列表,在 parameterMapping 的 mode 不为 IN 时,将 localOutputParameterCache 缓存对象的对应参数值更新到当前请求参数中;
public abstract class BaseExecutor implements Executor {
private void handleLocallyCachedOutputParameters(MappedStatement ms, CacheKey key, Object parameter,
BoundSql boundSql) {
if (ms.getStatementType() == StatementType.CALLABLE) {
final Object cachedParameter = localOutputParameterCache.getObject(key);
if (cachedParameter != null && parameter != null) {
final MetaObject metaCachedParameter = configuration.newMetaObject(cachedParameter);
final MetaObject metaParameter = configuration.newMetaObject(parameter);
for (ParameterMapping parameterMapping : boundSql.getParameterMappings()) {
if (parameterMapping.getMode() != ParameterMode.IN) {
final String parameterName = parameterMapping.getProperty();
final Object cachedValue = metaCachedParameter.getValue(parameterName);
metaParameter.setValue(parameterName, cachedValue);
}
}
}
}
}
}
queryFromDatabase 首先使用 EXECUTION_PLACEHOLDER 对缓存占位,接下来调用 doQuery 方法(BaseExecutor 并未实现,在具体实现类中实现)查询结果,查询完毕后移除占位的缓存;随后将查询结果添加到 localCache 缓存中,最后若 MappedStatement 参数的类型为 CALLABLE 时将查询入参添加到本地入参缓存对象(localOutputParameterCache)中;
public abstract class BaseExecutor implements Executor {
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;
}
}
2.2 queryCursor 查询方法
queryCursor 只做了两件事,第一件事为调用 MappedStatement 对象的 getBoundSql 方法获取 BoundSql 对象,第二件事则是调用 doQueryCursor 方法(与 doQuery 一样,BaseExecutor 中并未实现,在具体实现类中实现)获取结果集指针;
public abstract class BaseExecutor implements Executor {
@Override
public <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
return doQueryCursor(ms, parameter, rowBounds, boundSql);
}
}
2.3 update 更新方法
update 方法首先验证当前执行器是否关闭,随后调用 clearLocalCache 方法清空本地缓存,最后调用 doUpdate 方法执行更新,与其他 do 开头方法一样,BaseExecutor 中并未实现,在具体实现类中实现;
public abstract class BaseExecutor implements Executor {
@Override
public int update(MappedStatement ms, Object parameter) throws SQLException {
ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
if (closed) {
throw new ExecutorException("Executor was closed.");
}
clearLocalCache();
return doUpdate(ms, parameter);
}
}
3. 事务方法
3.1 getTransaction 方法
getTransaction 方法首先验证执行器是否关闭,未关闭时直接返回 transaction 属性对象;
public abstract class BaseExecutor implements Executor {
@Override
public Transaction getTransaction() {
if (closed) {
throw new ExecutorException("Executor was closed.");
}
return transaction;
}
}
3.2 commit 方法
commit 方法首先验证执行器是否关闭,接下来调用 clearLocalCache 方法清空缓存与flushStatements 方法清空 Statement,最后根据 required 入参判断是否调用 transaction 属性的 commit 方法提交事务;
public abstract class BaseExecutor implements Executor {
@Override
public void commit(boolean required) throws SQLException {
if (closed) {
throw new ExecutorException("Cannot commit, transaction is already closed");
}
clearLocalCache();
flushStatements();
if (required) {
transaction.commit();
}
}
}
3.3 rollback 方法
rollback 方法首先验证执行器是否关闭,接下来调用 clearLocalCache 方法清空缓存与flushStatements 方法清空 Statement,最后根据 required 入参判断是否调用 transaction 属性的 rollback 方法回滚事务;
public abstract class BaseExecutor implements Executor {
@Override
public void rollback(boolean required) throws SQLException {
if (!closed) {
try {
clearLocalCache();
flushStatements(true);
} finally {
if (required) {
transaction.rollback();
}
}
}
}
}
4. 缓存方法
4.1 createCacheKey 方法
createCacheKey 方法首先也是验证当前执行器是否关闭,随后使用 MappedStatement 参数的 id 、RowBounds 参数的偏移量及页面大小与 BoundSql 对象的 sql 语句创建 CacheKey 对象;随后遍历 BoundSql 对象的 parameterMappings 属性列表,在 parameterMapping 的 mode 为 OUT 时使用参数值、参数名对应扩展参数值或对象属性值对 CacheKey 对象进行更新,最后如果系统配置了 environment 则使用其 id 来更新 CacheKey 对象。
public abstract class BaseExecutor implements Executor {
@Override
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
if (closed) {
throw new ExecutorException("Executor was closed.");
}
CacheKey cacheKey = new CacheKey();
cacheKey.update(ms.getId());
cacheKey.update(rowBounds.getOffset());
cacheKey.update(rowBounds.getLimit());
cacheKey.update(boundSql.getSql());
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
// mimic DefaultParameterHandler logic
MetaObject metaObject = null;
for (ParameterMapping parameterMapping : parameterMappings) {
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 {
if (metaObject == null) {
metaObject = configuration.newMetaObject(parameterObject);
}
value = metaObject.getValue(propertyName);
}
cacheKey.update(value);
}
}
if (configuration.getEnvironment() != null) {
// issue #176
cacheKey.update(configuration.getEnvironment().getId());
}
return cacheKey;
}
}
4.2 clearLocalCache 方法
clearLocalCache 方法在当前执行器未关闭时将 localCache 与 localOutputParameterCache 缓存清空。
public abstract class BaseExecutor implements Executor {
@Override
public void clearLocalCache() {
if (!closed) {
localCache.clear();
localOutputParameterCache.clear();
}
}
}
4.3 isCached 方法
isCached 方法判断 localCache 中是否拥有指定缓存。
public abstract class BaseExecutor implements Executor {
@Override
public boolean isCached(MappedStatement ms, CacheKey key) {
return localCache.getObject(key) != null;
}
}
5. 其他方法
5.1 flushStatements 方法
flushStatements 方法在执行器未关闭时调用子类需实现的 doFlushStatements 方法刷新当前执行器。
public abstract class BaseExecutor implements Executor {
@Override
public List<BatchResult> flushStatements() throws SQLException {
return flushStatements(false);
}
public List<BatchResult> flushStatements(boolean isRollBack) throws SQLException {
if (closed) {
throw new ExecutorException("Executor was closed.");
}
return doFlushStatements(isRollBack);
}
}
5.2 setExecutorWrapper 方法
setExecutorWrapper 方法直接对 wrapper 属性赋值。
public abstract class BaseExecutor implements Executor {
@Override
public void setExecutorWrapper(Executor wrapper) {
this.wrapper = wrapper;
}
}
5.3 close 方法
close 方法首先调用 rollback 方法对事务进行回滚,随后调用 transaction 属性的 close 方法关闭事务,最后将 transaction、deferredLoads、localCache 以及 localOutputParameterCache 属性置为 null,同时将本执行器的 closed 状态置为 true;
public abstract class BaseExecutor implements Executor {
@Override
public void close(boolean forceRollback) {
try {
try {
rollback(forceRollback);
} finally {
if (transaction != null) {
transaction.close();
}
}
} catch (SQLException e) {
// Ignore. There's nothing that can be done at this point.
log.warn("Unexpected exception on closing transaction. Cause: " + e);
} finally {
transaction = null;
deferredLoads = null;
localCache = null;
localOutputParameterCache = null;
closed = true;
}
}
}
5.4 isClosed 方法
isClosed 方法直接返回 closed 属性值;
public abstract class BaseExecutor implements Executor {
@Override
public boolean isClosed() {
return closed;
}
}
6. DeferredLoad 内部类
BaseExecutor 类中的 DeferredLoad 内部类用于缓存与加载指定对象的指定属性值,其中 resultObject 属性用于存储指定对象的 MetaObject 包装对象、property 属性用于存储需要缓存的对象属性名、targetType 则是缓存属性的类型 Class 对、key 是 CacheKey 对象、localCache 为缓存存储对象、objectFactory 属性保存的是对象工厂对象以及 resultExtractor 保存的是根据缓存结果提取器对象;
6.1 对象创建
在 DeferredLoad 创建过程中,resultObject、property、key、localCache 以及 targetType 属性都是直接使用调用者传入的参数进行赋值,objectFactory 属性使用的是 configuration 对象中的对象工厂对象,resultExtractor 属性则是使用 configuration 参数与获取到的 objectFactory 对象进行创建;
public abstract class BaseExecutor implements Executor {
private static class DeferredLoad {
public DeferredLoad(MetaObject resultObject, String property, CacheKey key, PerpetualCache localCache,
Configuration configuration, Class<?> targetType) {
this.resultObject = resultObject;
this.property = property;
this.key = key;
this.localCache = localCache;
this.objectFactory = configuration.getObjectFactory();
this.resultExtractor = new ResultExtractor(configuration, objectFactory);
this.targetType = targetType;
}
}
}
6.2 方法解析
DeferredLoad 类中只有两个方法,分别是判断当前对象是否可加载的 canLoad 方法与加载当前缓存数据的 load 方法;
canLoad 方法判断缓存是否拥有 key 属性且缓存对象中 key 对应值是否不为占位符; load 方法首先从缓存中获取缓存值,然后调用 resultExtractor 属性的 extractObjectFromList 方法提取属性值,最后将缓存值置为缓存值;
public abstract class BaseExecutor implements Executor {
private static class DeferredLoad {
public boolean canLoad() {
return localCache.getObject(key) != null && localCache.getObject(key) != EXECUTION_PLACEHOLDER;
}
public void load() {
@SuppressWarnings("unchecked")
// we suppose we get back a List
List<Object> list = (List<Object>) localCache.getObject(key);
Object value = resultExtractor.extractObjectFromList(list, targetType);
resultObject.setValue(property, value);
}
}
}
6.3 extractObjectFromList 方法
extractObjectFromList 方法在 targetType 为 list、集合或者数组时,直接将 list 参数转化为相应对象然后返回,否则只有在 list 参数大小为 1 时才会将 list 参数的第一个元素返回,大小不为 0 则直接报错;
public class ResultExtractor {
public Object extractObjectFromList(List<Object> list, Class<?> targetType) {
Object value = null;
if (targetType != null && targetType.isAssignableFrom(list.getClass())) {
value = list;
} else if (targetType != null && objectFactory.isCollection(targetType)) {
value = objectFactory.create(targetType);
MetaObject metaObject = configuration.newMetaObject(value);
metaObject.addAll(list);
} else if (targetType != null && targetType.isArray()) {
Class<?> arrayComponentType = targetType.getComponentType();
Object array = Array.newInstance(arrayComponentType, list.size());
if (arrayComponentType.isPrimitive()) {
for (int i = 0; i < list.size(); i++) {
Array.set(array, i, list.get(i));
}
value = array;
} else {
value = list.toArray((Object[]) array);
}
} else if (list != null && list.size() > 1) {
throw new ExecutorException("Statement returned more than one row, where no more than one was expected.");
} else if (list != null && list.size() == 1) {
value = list.get(0);
}
return value;
}
}
三 SimpleExecutor 类
SimpleExecutor 类为简单查询的执行器,其没有自己的独立属性,实现了 BaseExecutor 中所有以 do 开头的方法;
1 doQuery 方法
doQuery 方法首先使用内部执行器属性 wrapper 、MappedStatement 参数、parameter 方法入参 、rowBounds 内部分页对象参数、resultHandler 结果处理器参数以及 boundSql sql 对象参数创建 StatementHandler 对象,然后调用 prepareStatement 方法创建 Statement 对象同时调用StatementHandler 的 query 方法进行查询,最后调用 closeStatement 关闭 Statement 对象;
public class SimpleExecutor extends BaseExecutor {
@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);
}
}
}
closeStatement 方法直接调用 statement 的 close 方法,关闭 statement 对象;
public abstract class BaseExecutor implements Executor {
protected void closeStatement(Statement statement) {
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
// ignore
}
}
}
}
prepareStatement 方法首先调用 getConnection 方法获取数据库连接对象,随后调用 handler 参数的 prepare 方法创建 Statement 对象,最后调用 StatementHandler 对象参数的 parameterize 方法将 Statement 对象保存到 StatementHandler 对象中;
public class SimpleExecutor extends BaseExecutor {
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;
}
}
getConnection 方法首先获取事务属性 transaction 中存储的数据库连接对象,然后若传入的日志 Log 对象在 Debug 状态下启用,返回使用 ConnectionLogger 创建使用日志对象封装的数据库连接对象,否则直接返回从事务对象 transaction 中获取的连接对象;
public class SimpleExecutor extends BaseExecutor {
protected Connection getConnection(Log statementLog) throws SQLException {
Connection connection = transaction.getConnection();
if (statementLog.isDebugEnabled()) {
return ConnectionLogger.newInstance(connection, statementLog, queryStack);
}
return connection;
}
}
2 doQueryCursor 方法
doQueryCursor 方法首先也是创建 StatementHandler 对象,然后调用 prepareStatement 方法创建 Statement 对象同时调用 StatementHandler 的 queryCursor 方法获取指针对象,最后调用 Statement 的 closeOnCompletion 方法在结果集处理完之后关闭 Statement;
public class SimpleExecutor extends BaseExecutor {
@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());
Cursor<E> cursor = handler.queryCursor(stmt);
stmt.closeOnCompletion();
return cursor;
}
}
3 doUpdate 方法
与 doQuery 一样,doUpdate 方法首先也是创建 StatementHandler 对象,但传入的执行为本执行器对象,不会传入 resultHandler 与 boundSql 对象;随后也是调用 prepareStatement 方法创建 Statement 对象同时执行 StatementHandler 的 update 方法,最后调用 closeStatement 关闭 Statement 对象;
public class SimpleExecutor extends BaseExecutor {
@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);
}
}
}
4 doFlushStatements 方法
doFlushStatements 方法直接返回空列表;
public class SimpleExecutor extends BaseExecutor {
@Override
public List<BatchResult> doFlushStatements(boolean isRollback) {
return Collections.emptyList();
}
}
四 ReuseExecutor 类
ReuseExecutor 类为一个可复用的执行器,与 SimpleExecutor 类在每次执行完 sql 都会关闭 Statement 对象不一样的是,在每次执行完 sql 之后不会关闭 Statement 对象而是将 sql 字符串与 Statement 对象的对照关系存储到 statementMap 对象中用于重复使用;
1 doQuery 方法
doQuery 方法执行逻辑与 SimpleExecutor 类中的 doQuery 方法一样,只是在最后不会关闭 Statement;
public class ReuseExecutor extends BaseExecutor {
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler,
BoundSql boundSql) throws SQLException {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler,
boundSql);
Statement stmt = prepareStatement(handler, ms.getStatementLog());
return handler.query(stmt, resultHandler);
}
}
ReuseExecutor 执行器的 prepareStatement 方法判断 statementMap 属性中是否存在 sql 字符串关联的 Statement 对象以及关联对象中的数据库连接是否已关闭,若为真则直接从缓存中获取对应的 Statement 对象同时使用配置的查询超时更新 Statement 对象的超时时间,否则与 SimpleExecutor 类一样创建 Statement 对象同时将 sql 字符串与 Statement 对象的映射保存到 statementMap 中;
public abstract class ReuseExecutor implements Executor {
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
BoundSql boundSql = handler.getBoundSql();
String sql = boundSql.getSql();
if (hasStatementFor(sql)) {
stmt = getStatement(sql);
applyTransactionTimeout(stmt);
} else {
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
putStatement(sql, stmt);
}
handler.parameterize(stmt);
return stmt;
}
private boolean hasStatementFor(String sql) {
try {
Statement statement = statementMap.get(sql);
return statement != null && !statement.getConnection().isClosed();
} catch (SQLException e) {
return false;
}
}
}
applyTransactionTimeout 方法直接调用 StatementUtil 中的 applyTransactionTimeout 静态方法更新 Statement 对象的超时时间,其入参为 Statement 对象,Statement 当前保存的超时时间以及 transaction 对象中的超时;StatementUtil 类的 applyTransactionTimeout 首先判断是否未传入事务超时,随后在 queryTimeout 为空、queryTimeout 为 0 或事务超时值小于 queryTimeout 时将 Statement 参数的查询超时更新为事务超时值。
public class BaseExecutor extends Executor {
protected void applyTransactionTimeout(Statement statement) throws SQLException {
StatementUtil.applyTransactionTimeout(statement, statement.getQueryTimeout(), transaction.getTimeout());
}
}
public class StatementUtil {
public static void applyTransactionTimeout(Statement statement, Integer queryTimeout, Integer transactionTimeout)
throws SQLException {
if (transactionTimeout == null) {
return;
}
if (queryTimeout == null || queryTimeout == 0 || transactionTimeout < queryTimeout) {
statement.setQueryTimeout(transactionTimeout);
}
}
}
doQueryCursor 与 doUpdate 方法与 SimpleExecutor 中的对应方法逻辑一样,只是在执行完之后不会调用 closeStatement 方法关闭 Statement 对象。
2 doFlushStatements 方法
doFlushStatements 方法也没有返回任何列表,与 SimpleExecutor 的差异在于在返回之前遍历 statementMap 保存的所有 Statement 对象并调用 closeStatement 方法关闭 Statement 对象;
public class ReuseExecutor extends BaseExecutor {
@Override
public List<BatchResult> doFlushStatements(boolean isRollback) {
for (Statement stmt : statementMap.values()) {
closeStatement(stmt);
}
statementMap.clear();
return Collections.emptyList();
}
}
五 BatchExecutor 类
BatchExecutor 执行器类用于批量执行更新 sql,其拥有一个用于保存 doUpdate 方法返回结果值的 BATCH_UPDATE_RETURN_VALUE 常量以及四个对象变量,分别为用于存储未执行更新的 Statement 对象的 statementList、用于存储未执行的 BatchResult 对象的 batchResultList 对象、之前最后一次执行的 sql 字符串的 currentSql 以及之前最后一次执行的 MappedStatement 对象的 currentStatement 参数。
public class BatchExecutor extends BaseExecutor {
public static final int BATCH_UPDATE_RETURN_VALUE = Integer.MIN_VALUE + 1002;
private final List<Statement> statementList = new ArrayList<>();
private final List<BatchResult> batchResultList = new ArrayList<>();
private String currentSql;
private MappedStatement currentStatement;
}
1 doQuery 方法
doQuery 方法除了为了保证查询的结果为最新,在查询之前调用 flushStatements 方法刷新之前保存的所有执行对象外,其实际执行逻辑与 SimpleExecutor 中的 doQuery 方法一致;
public class BatchExecutor extends BaseExecutor {
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameterObject, RowBounds rowBounds,
ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
flushStatements();
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameterObject, rowBounds,
resultHandler, boundSql);
Connection connection = getConnection(ms.getStatementLog());
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
}
2 doQueryCursor 方法
doQueryCursor 方法与 doQuery 方法一样,只是在 SimpleExecutor 中的 doQueryCursor 方法执行之前调用了一次 flushStatements 方法刷新之前保存的所有执行对象;
public class BatchExecutor extends BaseExecutor {
@Override
protected <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)
throws SQLException {
flushStatements();
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, null, boundSql);
Connection connection = getConnection(ms.getStatementLog());
Statement stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
Cursor<E> cursor = handler.queryCursor(stmt);
stmt.closeOnCompletion();
return cursor;
}
}
3 doUpdate 方法
doUpdate 方法首先创建 StatementHandler 对象,然后获取其中保存的 Sql 字符串;在 sql 字符串与上次为执行 sql 字符串一致且 ms 参数与上次未执行的 MappedStatement 对象一致时,首先获取上次使用的 Statement 对象,随后调用 applyTransactionTimeout 方法更新超时时间,最后获取对应 batchResult 对象并调用 addParameterObject 将方法入参添加到 batchResult 之中;
在 sql 字符串与上次为执行 sql 字符串不一致或 ms 参数与上次未执行的 MappedStatement 对象不一致时,则使用 StatementHandler 对象创建 Statement 对象并分别将 currentSql 属性更新为当前使用的 sql,currentStatement 属性更新为 ms 参数、将 stmt 对象添加到 statementList 属性中以及向 batchResultList 属性中添加使用 ms、sql 与 parameterObject 创建的 BatchResult 对象;
在处理完 Statement 对象后,调用 StatementHandler 的 batch 方法调用 Statement 对象中 addBatch 方法添加执行参数;
public class BatchExecutor extends BaseExecutor {
@Override
public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
final Configuration configuration = ms.getConfiguration();
final StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT,
null, null);
final BoundSql boundSql = handler.getBoundSql();
final String sql = boundSql.getSql();
final Statement stmt;
if (sql.equals(currentSql) && ms.equals(currentStatement)) {
int last = statementList.size() - 1;
stmt = statementList.get(last);
applyTransactionTimeout(stmt);
handler.parameterize(stmt);// fix Issues 322
BatchResult batchResult = batchResultList.get(last);
batchResult.addParameterObject(parameterObject);
} else {
Connection connection = getConnection(ms.getStatementLog());
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt); // fix Issues 322
currentSql = sql;
currentStatement = ms;
statementList.add(stmt);
batchResultList.add(new BatchResult(ms, sql, parameterObject));
}
handler.batch(stmt);
return BATCH_UPDATE_RETURN_VALUE;
}
}
4 doFlushStatements 方法
doFlushStatements 方法在 isRollback 参数为真,即进行事务回退时,直接返回空列表;否则的话遍历 statementList 参数,在遍历的过程中,首先调用 applyTransactionTimeout 方法刷新之前为执行的 Statement 对象的超时时间,然后获取对应的 BatchResult 对象同时执行 Statement 对象的 executeBatch 方法批量执行更新并将执行结果保存到 BatchResult 的 updateCounts 属性中;随后在当前 MappedStatement 对象关联 KeyGenerator 为 Jdbc3KeyGenerator 时,则执行其 processBatch 方法,否则只有在关联 KeyGenerator 不为 NoKeyGenerator 时,才会执行关联对象实现的 processAfter 方法,最后调用 closeStatement 方法关闭 Statement 对象;
在 statementList 遍历完成后,返回处理完的 BatchResult 列表,同时无论是否执行成功,都会将 currentSql 初始化为 null、statementList 以及 batchResultList 清空;
public class SimpleExecutor extends BaseExecutor {
@Override
public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
try {
List<BatchResult> results = new ArrayList<>();
if (isRollback) {
return Collections.emptyList();
}
for (int i = 0, n = statementList.size(); i < n; i++) {
Statement stmt = statementList.get(i);
applyTransactionTimeout(stmt);
BatchResult batchResult = batchResultList.get(i);
try {
batchResult.setUpdateCounts(stmt.executeBatch());
MappedStatement ms = batchResult.getMappedStatement();
List<Object> parameterObjects = batchResult.getParameterObjects();
KeyGenerator keyGenerator = ms.getKeyGenerator();
if (Jdbc3KeyGenerator.class.equals(keyGenerator.getClass())) {
Jdbc3KeyGenerator jdbc3KeyGenerator = (Jdbc3KeyGenerator) keyGenerator;
jdbc3KeyGenerator.processBatch(ms, stmt, parameterObjects);
} else if (!NoKeyGenerator.class.equals(keyGenerator.getClass())) { // issue #141
for (Object parameter : parameterObjects) {
keyGenerator.processAfter(this, ms, stmt, parameter);
}
}
// Close statement to close cursor #1109
closeStatement(stmt);
} catch (BatchUpdateException e) {
StringBuilder message = new StringBuilder();
message.append(batchResult.getMappedStatement().getId()).append(" (batch index #").append(i + 1).append(")")
.append(" failed.");
if (i > 0) {
message.append(" ").append(i)
.append(" prior sub executor(s) completed successfully, but will be rolled back.");
}
throw new BatchExecutorException(message.toString(), e, results, batchResult);
}
results.add(batchResult);
}
return results;
} finally {
for (Statement stmt : statementList) {
closeStatement(stmt);
}
currentSql = null;
statementList.clear();
batchResultList.clear();
}
}
}
六 CachingExecutor 类
CachingExecutor 类是使用装饰器模式对其他执行器的进行装饰,用于对其他执行器补充缓存功能;其拥有 delegate 内部执行器与 TransactionalCacheManager 事务缓存管理器属性两个特殊参数;
public class CachingExecutor implements Executor {
private final Executor delegate;
private final TransactionalCacheManager tcm = new TransactionalCacheManager();
public CachingExecutor(Executor delegate) {
this.delegate = delegate;
delegate.setExecutorWrapper(this);
}
}
其中对 Executor 接口的方法实现可以分为两类,一种是直接调用内部执行器 delegate 属性对应方法,包含 getTransaction、flushStatements、createCacheKey、isCached、deferLoad 以及 clearLocalCache 方法;
其余方法都是增强方法,其中 update 与 queryCursor 方法都是在调用内部对应方法直接调用 flushCacheIfRequired 方法在必要的情况下刷新缓存,flushCacheIfRequired 方法在缓存不为空且 ms 参数配置了需要刷新缓存时,调用 tcm 的 clear 方法清空对应缓存;
public class CachingExecutor implements Executor {
private void flushCacheIfRequired(MappedStatement ms) {
Cache cache = ms.getCache();
if (cache != null && ms.isFlushCacheRequired()) {
tcm.clear(cache);
}
}
}
1 query 查询方法
query 方法首先获取 ms 参数关联的 Cache 缓存对象,在缓存对象不为空时首先在需要时清空缓存,随后在当前 MappedStatement 使用了缓存且 resultHandler 结果处理器参数为空的情况下首先验证方法参数是否拥有出参,随后尝试从缓存中获取结果,在未获取结果时,则调用内部执行器进行查询,然后将查询结果保存到缓存中;
public class CachingExecutor implements Executor {
@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) {
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);
}
}
ensureNoOutParams 方法首先判断 ms 参数类型是否为 CALLABLE,为真时遍历 ms 参数的 ParameterMapping 列表,若存在任何部位 IN 类型的参数时报错;
public class CachingExecutor implements Executor {
private void ensureNoOutParams(MappedStatement ms, BoundSql boundSql) {
if (ms.getStatementType() == StatementType.CALLABLE) {
for (ParameterMapping parameterMapping : boundSql.getParameterMappings()) {
if (parameterMapping.getMode() != ParameterMode.IN) {
throw new ExecutorException(
"Caching stored procedures with OUT params is not supported. Please configure useCache=false in "
+ ms.getId() + " statement.");
}
}
}
}
}
2 事务方法
事务方法在调用内部执行器的相关方法的同时都会调用 tcm 属性的相关方法,如 close 方法中会调用 tcm 属性的 rollback 或 commit 方法;
public class CachingExecutor implements Executor {
@Override
public void close(boolean forceRollback) {
try {
// issues #499, #524 and #573
if (forceRollback) {
tcm.rollback();
} else {
tcm.commit();
}
} finally {
delegate.close(forceRollback);
}
}
@Override
public void commit(boolean required) throws SQLException {
delegate.commit(required);
tcm.commit();
}
@Override
public void rollback(boolean required) throws SQLException {
try {
delegate.rollback(required);
} finally {
if (required) {
tcm.rollback();
}
}
}
}