Mybatis3.5.1源码分析
- Mybatis-SqlSessionFactoryBuilder,XMLConfigBuilder,XPathParser源码解析
- Mybatis-Configuration源码解析
- Mybatis-事务对象源码解析
- Mybatis-数据源源码解析
- Mybatis缓存策略源码解析
- Mybatis-DatabaseIdProvider源码解析
- Mybatis-TypeHandler源码解析
- Mybatis-Reflector源码解析
- Mybatis-ObjectFactory,ObjectWrapperFactory源码分析
- Mybatis-Mapper各类标签封装类源码解析
- Mybatis-XMLMapperBuilder,XMLStatmentBuilder源码分析
- Mybatis-MapperAnnotationBuilder源码分析
- [Mybatis-MetaObject,MetaClass源码解析]https://www.jianshu.com/p/f51fa552f30a)
- Mybatis-LanguageDriver源码解析
- Mybatis-SqlSource源码解析
- Mybatis-SqlNode源码解析
- Mybatis-KeyGenerator源码解析
- Mybatis-Executor源码解析
- Mybatis-ParameterHandler源码解析
- Mybatis-StatementHandler源码解析
- Mybatis-DefaultResultSetHandler(一)源码解析
- Mybatis-DefaultResultSetHandler(二)源码解析
- Mybatis-ResultHandler,Cursor,RowBounds 源码分析
- Mybatis-MapperProxy源码解析
- Mybatis-SqlSession源码解析
- Mybatis-Interceptor源码解析
Executor
/**
* Copyright 2009-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.executor;
import java.sql.SQLException;
import java.util.List;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.transaction.Transaction;
/**
* Mybatis的执行器,用于控制ParameterHandler,StatementHandler,ResultSetHandler协调工作
*
* 参考博客:
*
*
* @author Clinton Begin
*/
public interface Executor {
/**
* 表示没有结果处理器的标记
*/
ResultHandler NO_RESULT_HANDLER = null;
/**
* update方法,传入MappedStatement 实例和参数,注意这里是不需要RowBounds参数和ResultHandler的
* @param ms Mapper.xml文件的select,delete,update,insert这些DML标签的封装类
* @param parameter 参数对象
* @return 影响的记录数
*/
int update(MappedStatement ms, Object parameter) throws SQLException;
/**
* 查询,先查缓存,再查数据库
* @param ms Mapper.xml文件的select,delete,update,insert这些DML标签的封装类
* @param parameter 参数对象
* @param rowBounds Mybatis的分页对象
* @param resultHandler 结果处理器对象
* @param cacheKey 缓存Key对象
* @param boundSql 参数映射与可执行SQL封装类对象
* @param 返回类型
*/
List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
/**
* 查询
* @param ms Mapper.xml文件的select,delete,update,insert这些DML标签的封装类
* @param parameter 参数对象
* @param rowBounds Mybatis的分页对象
* @param resultHandler 结果处理器对象
* @param 返回类型
*/
List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
/**
* 游标查询
* @param ms Mapper.xml文件的select,delete,update,insert这些DML标签的封装类
* @param parameter 参数对象
* @param rowBounds Mybatis的分页对象
* @param 返回类型
*/
Cursor queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
/**
* 刷新Statements
* @return
* @throws SQLException
*/
List flushStatements() throws SQLException;
/**
* 事务提交
* @param required
*/
void commit(boolean required) throws SQLException;
/**
* 事务回滚
* @param required
*/
void rollback(boolean required) throws SQLException;
/**
* 创建缓存的键对象
* @param ms Mapper.xml文件的select,delete,update,insert这些DML标签的封装类
* @param parameterObject 参数对象
* @param rowBounds Mybatis的分页对象
* @param boundSql 参数映射与可执行SQL封装类对象
*/
CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
/**
* 缓存中是否有这个查询的结果
* @param ms Mapper.xml文件的select,delete,update,insert这些DML标签的封装类
* @param key 每次查询操作的特征值抽象而成的类对象
*/
boolean isCached(MappedStatement ms, CacheKey key);
/**
* 清空一级缓存
*/
void clearLocalCache();
/**
* 延期加载
* @param ms Mapper.xml文件的select,delete,update,insert这些DML标签的封装类
* @param resultObject 结果元对象
* @param property 属性
* @param key 每次查询操作的特征值抽象而成的类对象
* @param targetType 目标类型
*/
void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class> targetType);
/**
* 获取事务
* @return 事务对象
*/
Transaction getTransaction();
/**
* 关闭执行器,回退事务,关闭事务
* @param forceRollback 是否强制回滚
*/
void close(boolean forceRollback);
/**
* 是否已经关闭
*/
boolean isClosed();
/**
* 设置执行器包装类
* @param executor 执行器
*/
void setExecutorWrapper(Executor executor);
}
BaseExecutor
/**
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.executor;
import static org.apache.ibatis.executor.ExecutionPlaceholder.EXECUTION_PLACEHOLDER;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.cache.impl.PerpetualCache;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.statement.StatementUtil;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.logging.jdbc.ConnectionLogger;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.mapping.StatementType;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.LocalCacheScope;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.transaction.Transaction;
import org.apache.ibatis.type.TypeHandlerRegistry;
/**
* @author Clinton Begin
*/
public abstract class BaseExecutor implements Executor {
private static final Log log = LogFactory.getLog(BaseExecutor.class);
/**
* 事务对象
*/
protected Transaction transaction;
/**
* 执行器包装类
*/
protected Executor wrapper;
/**
* 延期加载队列
*
* ConcurrentLinkedQueue:
*
* - 一个基于链接节点的无界线程安全队列。此队列按照 FIFO(先进先出)原则对元素进行排序。队列的头部 是队列中时间最长的元素。队列的尾部 是队列中时间最短的元素。
* - 新的元素插入到队列的尾部,队列获取操作从队列头部获得元素。当多个线程共享访问一个公共 collection 时,ConcurrentLinkedQueue 是一个恰当的选择。此队列不允许使用 null 元素。
*
*
*/
protected ConcurrentLinkedQueue deferredLoads;
/**
* 本地缓存,一级缓存
*/
protected PerpetualCache localCache;
/**
* 本地输出参数缓存
*/
protected PerpetualCache localOutputParameterCache;
/**
* mybatis全局配置信息类
*/
protected Configuration configuration;
protected int queryStack;
/**
* 执行器是否已经关闭标记
*/
private boolean closed;
/**
*
* @param configuration mybatis全局配置信息
* @param transaction 事务
*/
protected BaseExecutor(Configuration configuration, Transaction transaction) {
//初始化事务
this.transaction = transaction;
this.deferredLoads = new ConcurrentLinkedQueue<>();
//初始化一个id为LocalCache的缓存对象
this.localCache = new PerpetualCache("LocalCache");
//初始化一个id为LocalOutputParameterCache的缓存对象
this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");
//标识自己未关闭
this.closed = false;
// 初始化配置对象
this.configuration = configuration;
// 包装对象先初始化为自己,另外有一个重写的方法setExecutorWrapper可以重置wrapper对象
this.wrapper = this;
}
/**
* 获取事务,当执行器关闭时抛出异常
* @return {@link #transaction}
*/
@Override
public Transaction getTransaction() {
if (closed) {
throw new ExecutorException("Executor was closed.");
}
return transaction;
}
/**
* 关闭执行器,回退事务,关闭事务
* @param forceRollback 是否强制回退事务
*/
@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;
}
}
@Override
public boolean isClosed() {
return closed;
}
/**
* 更新
* @param ms Mapper.xml文件的select,delete,update,insert这些DML标签的封装类
* @param parameter 参数对象
* @return 影响的记录数
*/
@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);
}
/**
* 直接调用 {@link BaseExecutor#flushStatements(boolean)} ,参数写死为false,表示不回退事务
*/
@Override
public List flushStatements() throws SQLException {
return flushStatements(false);
}
/**
* 如果不是回退,执行全部保存在执行器中的Statment对象,并将结果封装到BatchResult集合中;
* 否则不再执行全部保存在执行器中的Statment对象,直接返回空集合
* 作用于批处理
* 每当调用commit、rollback、close方法时,都会先调用该方法,然后再commit、rollback、close。
* @param isRollBack 是否回退
*/
public List flushStatements(boolean isRollBack) throws SQLException {
if (closed) {
throw new ExecutorException("Executor was closed.");
}
return doFlushStatements(isRollBack);
}
/**
* 查询,并创建好CacheKey对象
* @param ms Mapper.xml文件的select,delete,update,insert这些DML标签的封装类
* @param parameter 参数对象
* @param rowBounds Mybatis的分页对象
* @param resultHandler 结果处理器对象
*/
@Override
public List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//从MappedStatement对象中根据参数获取BoundSql对象
BoundSql boundSql = ms.getBoundSql(parameter);
// 创建缓存Key
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
// 调用重载的query方法
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
/**
* 查询
* @param ms Mapper.xml文件的select,delete,update,insert这些DML标签的封装类
* @param parameter 参数对象
* @param rowBounds Mybatis的分页对象
* @param resultHandler 结果处理器对象
* @param key 缓存对象
* @param boundSql 参数映射与可执行SQL封装类对象
*/
@SuppressWarnings("unchecked")
@Override
public List 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.");
}
//如果配置了flushCacheRequired为true,则会在执行器执行之前就清空本地一级缓存,换句话说就是关闭一级缓存功能
if (queryStack == 0 && ms.isFlushCacheRequired()) {
//查询堆栈为0且需要清空缓存,则执行清空缓存
clearLocalCache();
}
List list;
try {
//查询堆栈 + 1
queryStack++;
//如果此次查询的resultHandler为null(默认为null),则尝试从本地缓存中获取已经缓存的的结果,否则为null
list = resultHandler == null ? (List) localCache.getObject(key) : null;
//判断是否有已缓存的结果
if (list != null) {
//如果查到localCache缓存,处理localOutputParameterCache
handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
} else {
//没有缓存结果,则从数据库查询结果
list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
} finally {
// 查询堆栈数 -1
queryStack--;
}
if (queryStack == 0) {
//从延迟加载集合做,遍历加载数据到list中
for (DeferredLoad deferredLoad : deferredLoads) {
deferredLoad.load();
}
// issue #601
deferredLoads.clear();
if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
// issue #482
clearLocalCache();
}
}
return list;
}
@Override
public Cursor queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException {
//将参数传入boundSql
BoundSql boundSql = ms.getBoundSql(parameter);
return doQueryCursor(ms, parameter, rowBounds, boundSql);
}
@Override
public void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class> targetType) {
if (closed) {
throw new ExecutorException("Executor was closed.");
}
DeferredLoad deferredLoad = new DeferredLoad(resultObject, property, key, localCache, configuration, targetType);
//如果能加载则立即加载,否则加入到延迟加载队列中
if (deferredLoad.canLoad()) {
deferredLoad.load();
} else {
//添加到延迟加载队列中,这里重新new一个DeferredLoad对象没理解为啥。
deferredLoads.add(new DeferredLoad(resultObject, property, key, localCache, configuration, targetType));
}
}
@Override
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
if (closed) {
throw new ExecutorException("Executor was closed.");
}
CacheKey cacheKey = new CacheKey();
// MappedStatement的id
cacheKey.update(ms.getId());
// 分页参数的offset
cacheKey.update(rowBounds.getOffset());
// 分页参数的limit
cacheKey.update(rowBounds.getLimit());
// SQL语句本身
cacheKey.update(boundSql.getSql());
// 传递给jdbc的参数
List parameterMappings = boundSql.getParameterMappings();
//类型处理注册器
TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
// mimic DefaultParameterHandler logic
for (ParameterMapping parameterMapping : parameterMappings) {
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();//获取对应映射名
if (boundSql.hasAdditionalParameter(propertyName)) {
//获取propertyName的额外映射值
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
//如果参数对象为null,value即为null
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
//如果存在parameterObject对应类型处理器,value即为parameterObject
value = parameterObject;
} else {
//从元对象中获取propertyName的值
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
cacheKey.update(value);
}
}
if (configuration.getEnvironment() != null) {
// issue #176 //获取环境Id,因为有可能配置了多个环境,所以要加上环境Id做标识
cacheKey.update(configuration.getEnvironment().getId());
}
return cacheKey;
}
/**
* 是否已经缓存
* @param ms Mapper.xml文件的select,delete,update,insert这些DML标签的封装类
* @param key 每次查询操作的特征值抽象而成的类对象
*/
@Override
public boolean isCached(MappedStatement ms, CacheKey key) {
//如果从本地缓存集合中获取key对应的对象不为null
return localCache.getObject(key) != null;
}
/**
* 提交前,清空一级缓存和刷新Statements
* @param required 如果为true,提交事务
*/
@Override
public void commit(boolean required) throws SQLException {
if (closed) {
throw new ExecutorException("Cannot commit, transaction is already closed");
}
//清空一级缓存
clearLocalCache();
//刷新Statements
flushStatements();
if (required) {
transaction.commit();
}
}
/**
* 回退前,清空一级缓存和刷新Statements
* @param required 如果为true,回退事务
*/
@Override
public void rollback(boolean required) throws SQLException {
if (!closed) {
try {
//清空一级缓存
clearLocalCache();
//刷新Statements,并回退
flushStatements(true);
} finally {
if (required) {
transaction.rollback();
}
}
}
}
/**
* 清空一级缓存,只有在执行器关闭以后才会有效果
*/
@Override
public void clearLocalCache() {
if (!closed) {
//清除本地缓存
localCache.clear();
//清除输出参数缓存
localOutputParameterCache.clear();
}
}
/**
* 执行[insert][update][delete]SQL,并返回更新记录数
* @param ms Mapper.xml文件的select,delete,update,insert这些DML标签的封装类
* @param parameter 参数对象
* @return 更新记录数
*/
protected abstract int doUpdate(MappedStatement ms, Object parameter)
throws SQLException;
/**
* 如果不是回退,执行全部保存在执行器中的Statment对象,并将结果封装到BatchResult集合中;
* 否则不再执行全部保存在执行器中的Statment对象,直接返回空集合
* 作用于批处理
* 每当调用commit、rollback、close方法时,都会先调用该方法,然后再commit、rollback、close。
* @param isRollback 是否回退
*/
protected abstract List doFlushStatements(boolean isRollback)
throws SQLException;
/**
* 执行查询SQL,返回结果对象集合
* @param ms Mapper.xml文件的select,delete,update,insert这些DML标签的封装类
* @param parameter 参数对象
* @param rowBounds Mybatis的分页对象
* @param resultHandler 结果处理器对象
* @param boundSql 参数映射与可执行SQL封装类对象
*/
protected abstract List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException;
/**
* 执行查询SQL,返回游标对象
* @param ms Mapper.xml文件的select,delete,update,insert这些DML标签的封装类
* @param parameter 参数对象
* @param rowBounds Mybatis的分页对象
* @param boundSql 参数映射与可执行SQL封装类对象,已将参数对象加入到boundSql中
*/
protected abstract Cursor doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql)
throws SQLException;
/**
* 关闭Statement,捕捉了关闭Statement的SQLException但不进行处理
* @param statement 执行 SQL 语句并返回它所生成结果的对象
*/
protected void closeStatement(Statement statement) {
//如果statemenet不为null
if (statement != null) {
try {
//关闭statement
statement.close();
} catch (SQLException e) {
// ignore 忽略关闭statement的SQLException
}
}
}
/**
* Apply a transaction timeout. 应用事务超时。事务超时大于查询时间,则不做更改;否则将查询时间修改成事务时间
* @param statement a current statement 当前的Statmet
* @throws SQLException if a database access error occurs, this method is called on a closed Statement
* @since 3.4.0
* @see StatementUtil#applyTransactionTimeout(Statement, Integer, Integer)
*/
protected void applyTransactionTimeout(Statement statement) throws SQLException {
StatementUtil.applyTransactionTimeout(statement, statement.getQueryTimeout(), transaction.getTimeout());
}
/**
* 将本地缓存输出参数对象赋值到paramter中
* @param ms Mapper.xml文件的select,delete,update,insert这些DML标签的封装类
* @param key 缓存对象
* @param parameter 参数对象
* @param boundSql 参数映射与可执行SQL封装类对象
*/
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();
//从缓存参数元对象中获取parameterName的值,并通过metaParamter赋值到parameter
final Object cachedValue = metaCachedParameter.getValue(parameterName);
metaParameter.setValue(parameterName, cachedValue);
}
}
}
}
}
/**
* 从数据库中查找出来
* @param ms Mapper.xml文件的select,delete,update,insert这些DML标签的封装类
* @param parameter 参数对象
* @param rowBounds Mybatis的分页对象
* @param resultHandler 结果处理器对象
* @param key 缓存Key对象
* @param boundSql 参数映射与可执行SQL封装类对象
* @param 返回类型
*/
private List queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
List 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
localOutputParameterCache.putObject(key, parameter);
}
return list;
}
/**
* 获取数据库连接
* @param statementLog stat
* @return
* @throws SQLException
*/
protected Connection getConnection(Log statementLog) throws SQLException {
Connection connection = transaction.getConnection();
if (statementLog.isDebugEnabled()) {
//开启了Dugger模式,会新建Connection代理对象
return ConnectionLogger.newInstance(connection, statementLog, queryStack);
} else {
return connection;
}
}
@Override
public void setExecutorWrapper(Executor wrapper) {
this.wrapper = wrapper;
}
/**
* 延迟加载类
*/
private static class DeferredLoad {
/**
* 结果对象
*/
private final MetaObject resultObject;
/**
* 属性名
*/
private final String property;
/**
* 目标类型
*/
private final Class> targetType;
/**
* 缓存Key
*/
private final CacheKey key;
/**
* 本地缓存
*/
private final PerpetualCache localCache;
/**
* 对象工厂,从mybatis全局配置信息中获取
*/
private final ObjectFactory objectFactory;
/**
* 结果提取器
*/
private final ResultExtractor resultExtractor;
// issue #781
/**
*
* @param resultObject 结果对象
* @param property 属性名
* @param key 缓存Key
* @param localCache 本地缓存
* @param configuration mybatis全局配置新
* @param targetType 目标类型
*/
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;
}
/**
* 是否可以加载
*
* 从 {@link #localCache} 中获取 {@link #key} 不为null且获取出来的值不是 {@link ExecutionPlaceholder#EXECUTION_PLACEHOLDER} (占位符) 就认为是可加载的。
*
* @return true为可加载,否则为false
*/
public boolean canLoad() {
return localCache.getObject(key) != null && localCache.getObject(key) != EXECUTION_PLACEHOLDER;
}
/**
* 从 {@link #localCache} 中获取 {@link #key} 的记录数据,并根据 {@link #targetType}
* 将记录数据加入到targetType的对象,并将targetType对象加入resultObject,键名
* 为 {@link #property}
*/
public void load() {
@SuppressWarnings("unchecked")
// we suppose we get back a List
List
SimpleExecutor
/**
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.executor;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
import java.util.List;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.transaction.Transaction;
/**
* 简单执行器
* @author Clinton Begin
*/
public class SimpleExecutor extends BaseExecutor {
/**
*
* @param configuration mybatis全局配置信息
* @param transaction 事务对象
*/
public SimpleExecutor(Configuration configuration, Transaction transaction) {
super(configuration, transaction);
}
/**
* 执行[insert][update][delete]SQL,并返回更新记录数
* @param ms Mapper.xml文件的select,delete,update,insert这些DML标签的封装类
* @param parameter 参数对象
* @return 更新的记录数
*/
@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
try {
//获取mybatis全局配置信息
Configuration configuration = ms.getConfiguration();
//构建一个RoutingStatementHandler对象
/**
* RountingStatementHandler:一个具体实现类,这个类中并没有对Statement对象具体使用,还是根据得到Excutor类型,
* 决定创建何种类型StatementHandler对象。在MyBatis工作时,使用的StatementHandler接口对象实际上就是RoutingStatementHandler对象
*/
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
//构建Statement对象,并将参数值传入Statement对象中
stmt = prepareStatement(handler, ms.getStatementLog());
//通知stmt将[insert,update,delete]推送到数据库,并返回更新记录数
return handler.update(stmt);
} finally {
// 关闭Statement,捕捉了关闭Statement的SQLException但不进行处理
closeStatement(stmt);
}
}
/**
* 执行查询SQL,返回结果对象集合
* @param ms Mapper.xml文件的select,delete,update,insert这些DML标签的封装类
* @param parameter 参数对象
* @param rowBounds Mybatis的分页对象
* @param resultHandler 结果处理器对象
* @param boundSql 参数映射与可执行SQL封装类对象
* @param 结果对象
* @return 结果对象集合
*/
@Override
public List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
//获取mybatis全局配置信息
Configuration configuration = ms.getConfiguration();
//构建一个RoutingStatementHandler对象
/**
* RountingStatementHandler:一个具体实现类,这个类中并没有对Statement对象具体使用,还是根据得到Excutor类型,
* 决定创建何种类型StatementHandler对象。在MyBatis工作时,使用的StatementHandler接口对象实际上就是RoutingStatementHandler对象
*/
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//构建Statement对象,并将参数值传入Statement对象中
stmt = prepareStatement(handler, ms.getStatementLog());
//通知Statement将[select]推送到数据库并返回对应查询结果
return handler.query(stmt, resultHandler);
} finally {
// 关闭Statement,捕捉了关闭Statement的SQLException但不进行处理
closeStatement(stmt);
}
}
/**
* 执行查询SQL,返回游标对象
* @param ms Mapper.xml文件的select,delete,update,insert这些DML标签的封装类
* @param parameter 参数对象
* @param rowBounds Mybatis的分页对象
* @param boundSql 参数映射与可执行SQL封装类对象,已将参数对象加入到boundSql中
* @param 结果对象类型
* @return 存放结果对象的游标
*/
@Override
protected Cursor doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException {
//获取mybatis全局配置信息
Configuration configuration = ms.getConfiguration();
//构建一个RoutingStatementHandler对象
/**
* RountingStatementHandler:一个具体实现类,这个类中并没有对Statement对象具体使用,还是根据得到Excutor类型,
* 决定创建何种类型StatementHandler对象。在MyBatis工作时,使用的StatementHandler接口对象实际上就是RoutingStatementHandler对象
*/
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, null, boundSql);
//构建Statement对象,并将参数值传入Statement对象中
Statement stmt = prepareStatement(handler, ms.getStatementLog());
//若Statement执行了该方法,则当所有依赖于该Statement的ResultSet关闭时,该Statement会自动关闭。
stmt.closeOnCompletion();
//通知stmt将[select]推送到数据库并返回对应游标结果
return handler.queryCursor(stmt);
}
/**
* 如果不是回退,执行全部保存在执行器中的Statment对象,并将结果封装到BatchResult集合中;
* 否则不再执行全部保存在执行器中的Statment对象,直接返回空集合
* 作用于批处理
* 每当调用commit、rollback、close方法时,都会先调用该方法,然后再commit、rollback、close。
* @param isRollback 是否回退
*/
@Override
public List doFlushStatements(boolean isRollback) {
/**
* SimpleExecutor没有缓存Statement对象,每执行一次update或select,就开启一个Statement对象,
* 用完立刻关闭Statement对象。根据对应的sql直接执行即可,不会做一些额外的操作。
*/
//返回空集合
return Collections.emptyList();
}
/**
* 构建Statement对象,并将参数值传入Statement对象中
* @param handler Statement处理器
* @param statementLog MappedStatement对象的日志属性对象
* @return Statement对象
*/
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
//获取数据库连接
Connection connection = getConnection(statementLog);
//根据MappedStatement对象配置的StatementType,去创建Statement对象或则PreparedStatment或则CallableStatment
stmt = handler.prepare(connection, transaction.getTimeout());
//将参数值插入stmt
handler.parameterize(stmt);
return stmt;
}
}
BatchExecutor
/**
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.executor;
import java.sql.BatchUpdateException;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.transaction.Transaction;
/**
* 批量执行器
* @author Jeff Butler
*/
public class BatchExecutor extends BaseExecutor {
/**
* 批量更新返回值,表示最大批量执行条数
*/
public static final int BATCH_UPDATE_RETURN_VALUE = Integer.MIN_VALUE + 1002;
/**
* Statement对象集合
*/
private final List statementList = new ArrayList<>();
/**
* 批处理结果对象集合
*/
private final List batchResultList = new ArrayList<>();
/**
* 当前可执行SQL
*/
private String currentSql;
/**
* 当前 Mapper.xml文件的select,delete,update,insert这些DML标签的封装类
*/
private MappedStatement currentStatement;
/**
*
* @param configuration mybatis全局配置信息
* @param transaction 事务对象
*/
public BatchExecutor(Configuration configuration, Transaction transaction) {
super(configuration, transaction);
}
/**
* 执行[insert][update][delete]SQL,并返回更新记录数
* @param ms Mapper.xml文件的select,delete,update,insert这些DML标签的封装类
* @param parameterObject 参数对象
* @return 更新记录数
*/
@Override
public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
//mybatis全局配置信息
final Configuration configuration = ms.getConfiguration();
//构建一个RoutingStatementHandler对象
/**
* RountingStatementHandler:一个具体实现类,这个类中并没有对Statement对象具体使用,还是根据得到Excutor类型,
* 决定创建何种类型StatementHandler对象。在MyBatis工作时,使用的StatementHandler接口对象实际上就是RoutingStatementHandler对象
*/
final StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT, null, null);
//获取参数映射与可执行SQL封装类对象
final BoundSql boundSql = handler.getBoundSql();
//获取可执行SQL
final String sql = boundSql.getSql();
final Statement stmt;
//如果sql等于当前sql 且 ms等于当前DML标签封装类对象
if (sql.equals(currentSql) && ms.equals(currentStatement)) {
//获取Statement对象集合的最后一个Statement对象位置
int last = statementList.size() - 1;
//获取Statement对象集合的最后一个Statement对象
stmt = statementList.get(last);
//设置stmt的事务超时时间
applyTransactionTimeout(stmt);
//将参数值传入stmt
handler.parameterize(stmt);//fix Issues 322
//获取批量结果对象集合最后一个批量结果对象
BatchResult batchResult = batchResultList.get(last);
//将参数对象添加到批量结果对象中
batchResult.addParameterObject(parameterObject);
//如果sql不等于当前sql 或者 ms不等于当前DML标签封装类对象
} else {
//获取数据库连接对象
Connection connection = getConnection(ms.getStatementLog());
//构建Statement对象,并设置事务超时时长
stmt = handler.prepare(connection, transaction.getTimeout());
//将参数值传入stmt
handler.parameterize(stmt); //fix Issues 322
//设置当前sql为sql
currentSql = sql;
//设置当前DML标签封装类对象为ms
currentStatement = ms;
//添加stmt到Statement对象集合
statementList.add(stmt);
/**
* BatchResult:批处理结果,简单的POJO对象,
*/
//构建BatchResult对象并添加到batchResult集合中
batchResultList.add(new BatchResult(ms, sql, parameterObject));
}
// 将 {@code statement} 添加到批处理队列
handler.batch(stmt);
//写死返回最大批量执行条数
return BATCH_UPDATE_RETURN_VALUE;
}
/**
* 执行查询SQL,返回结果对象集合
* @param ms Mapper.xml文件的select,delete,update,insert这些DML标签的封装类
* @param parameterObject 参数对象
* @param rowBounds Mybatis的分页对象
* @param resultHandler 结果处理器对象
* @param boundSql 参数映射与可执行SQL封装类对象
* @param 结果对象类型
* @return 结果对象集合
*/
@Override
public List doQuery(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)
throws SQLException {
Statement stmt = null;
try {
//执行全部保存在执行器中的Statment对象,不回退事务,并将结果封装到BatchResult集合中
flushStatements();
//获取mybatis全局配置信息
Configuration configuration = ms.getConfiguration();
//构建一个RoutingStatementHandler对象
/**
* RountingStatementHandler:一个具体实现类,这个类中并没有对Statement对象具体使用,还是根据得到Excutor类型,
* 决定创建何种类型StatementHandler对象。在MyBatis工作时,使用的StatementHandler接口对象实际上就是RoutingStatementHandler对象
*/
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameterObject, rowBounds, resultHandler, boundSql);
//获取数据库连接
Connection connection = getConnection(ms.getStatementLog());
//构建Statement对象,并设置statement对象的查询超时时间
stmt = handler.prepare(connection, transaction.getTimeout());
//将参数值赋值到stmt里
handler.parameterize(stmt);
//通知stmt将[select]推送到数据库并返回对应查询结果
return handler.query(stmt, resultHandler);
} finally {
//关闭Statement,捕捉了关闭Statement的SQLException但不进行处理
closeStatement(stmt);
}
}
/**
* 执行查询SQL,返回游标对象
* @param ms Mapper.xml文件的select,delete,update,insert这些DML标签的封装类
* @param parameter 参数对象
* @param rowBounds Mybatis的分页对象
* @param boundSql 参数映射与可执行SQL封装类对象,已将参数对象加入到boundSql中
* @param 结果对象类型
* @return 结果对象游标
*/
@Override
protected Cursor doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException {
//执行全部保存在执行器中的Statment对象,不回退事务,并将结果封装到BatchResult集合中
flushStatements();
//获取mybatis全局配置信息
Configuration configuration = ms.getConfiguration();
//构建一个RoutingStatementHandler对象
/**
* RountingStatementHandler:一个具体实现类,这个类中并没有对Statement对象具体使用,还是根据得到Excutor类型,
* 决定创建何种类型StatementHandler对象。在MyBatis工作时,使用的StatementHandler接口对象实际上就是RoutingStatementHandler对象
*/
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, null, boundSql);
//获取数据库连接
Connection connection = getConnection(ms.getStatementLog());
//构建Statement对象,并设置statement对象的查询超时时间
Statement stmt = handler.prepare(connection, transaction.getTimeout());
//当stmt执行完后关闭
stmt.closeOnCompletion();
//将参数值赋值到stmt里
handler.parameterize(stmt);
//通知Statement将[select]推送到数据库并返回对应游标结果
return handler.queryCursor(stmt);
}
/**
* 如果不是回退,执行全部保存在执行器中的Statment对象,并将结果封装到BatchResult集合中;
* 否则不再执行全部保存在执行器中的Statment对象,直接返回空集合
* 作用于批处理
* 每当调用commit、rollback、close方法时,都会先调用该方法,然后再commit、rollback、close。
* @param isRollback 是否回退
* @return 批处理结果对象集合
*/
@Override
public List doFlushStatements(boolean isRollback) throws SQLException {
try {
//创建批量结果对象集合
List results = new ArrayList<>();
//如果是回退
if (isRollback) {
//不再执行执行全部保存在执行器中的Statment对象,直接返回空集合
return Collections.emptyList();
}
//下面代码只有提交时才会执行
//遍历statement对象集合
for (int i = 0, n = statementList.size(); i < n; i++) {
//获取Statement对象
Statement stmt = statementList.get(i);
//应用事务超时。事务超时大于查询时间,则不做更改;否则将查询时间修改成事务时间
applyTransactionTimeout(stmt);
//获取批处理结果
BatchResult batchResult = batchResultList.get(i);
try {
/**
* executeBatch:将一批SQL脚本提交给数据库来执行,如果全部命令执行成功,则返回更新计数组成的数组
*/
//执行SQL,将每个SQL执行完的更新记录数所组成的数组赋值到batchResult
batchResult.setUpdateCounts(stmt.executeBatch());
//获取DML标签封装对象
MappedStatement ms = batchResult.getMappedStatement();
//获取参数对象集合
List parameterObjects = batchResult.getParameterObjects();
//获取Key生成器
KeyGenerator keyGenerator = ms.getKeyGenerator();
//如果Key生成器是Jdbc3KeyGenerator.class
if (Jdbc3KeyGenerator.class.equals(keyGenerator.getClass())) {
//将keyGenerator强转为Jdbc3KeyGenerator对象
Jdbc3KeyGenerator jdbc3KeyGenerator = (Jdbc3KeyGenerator) keyGenerator;
//将 stmt 返回的主键赋值到 parameterObjects
jdbc3KeyGenerator.processBatch(ms, stmt, parameterObjects);
//如果Key生成器不是NoKeyGenerator类
} else if (!NoKeyGenerator.class.equals(keyGenerator.getClass())) { //issue #141
//变量参数对象集合
for (Object parameter : parameterObjects) {
//通知Key生成,执行执行完stmt后的业务逻辑
keyGenerator.processAfter(this, ms, stmt, parameter);
}
}
// Close statement to close cursor #1109 关闭语句关闭游标;
//关闭statement
closeStatement(stmt);
} catch (BatchUpdateException e) {
//拼装异常信息描述,描述是第几个statement对象执行是抛出异常,告诉用户因为执行不成功,将会被回退
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);
}
//将batchResult对象添加批量结果对象集合
results.add(batchResult);
}
return results;
} finally {
//遍历Statement对象
for (Statement stmt : statementList) {
//关闭Statement对象
closeStatement(stmt);
}
//将当前SQL置为null
currentSql = null;
//清空statement集合
statementList.clear();
//清空批处理结果对象集合
batchResultList.clear();
}
}
}
ReuseExecutor
/**
* Copyright 2009-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.executor;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.transaction.Transaction;
/**
*
* 作用于重复使用执行,其定义了一个Map,将执行的sql作为key,
* 将执行的Statement作为value保存,这样执行相同的sql时就可以使用已经存在的Statement,就不需要新创建了
*
*
* 在执行commit,rollback,close,flushStatements方法时,才会对这个缓存Map进行遍历元素逐一关闭,
* 然后清空缓存Map里的元素
*
* @author Clinton Begin
*/
public class ReuseExecutor extends BaseExecutor {
/**
* 存储SQL与Statement对象关系映射的映射集合,这里直接简称为statement映射集合
*/
private final Map statementMap = new HashMap<>();
/**
*
* @param configuration mybatis全局配置信息
* @param transaction 事务对象
*/
public ReuseExecutor(Configuration configuration, Transaction transaction) {
super(configuration, transaction);
}
/**
* 执行[insert][update][delete]SQL,并返回更新记录数
* @param ms Mapper.xml文件的select,delete,update,insert这些DML标签的封装类
* @param parameter 参数对象
* @return 更新记录数
*/
@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
//获取全局配置信息
Configuration configuration = ms.getConfiguration();
//构建一个RoutingStatementHandler对象
/**
* RountingStatementHandler:一个具体实现类,这个类中并没有对Statement对象具体使用,还是根据得到Excutor类型,
* 决定创建何种类型StatementHandler对象。在MyBatis工作时,使用的StatementHandler接口对象实际上就是RoutingStatementHandler对象
*/
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
//构建Statement对象,并将参数值传入Statement对象中
Statement stmt = prepareStatement(handler, ms.getStatementLog());
//通知stmt将[insert,update,delete]推送到数据库,并返回更新记录数
return handler.update(stmt);
//并没有关闭stmt
}
/**
*
* @param ms Mapper.xml文件的select,delete,update,insert这些DML标签的封装类
* @param parameter 参数对象
* @param rowBounds Mybatis的分页对象
* @param resultHandler 结果处理器对象
* @param boundSql 参数映射与可执行SQL封装类对象
* @param 结果对象类型
* @return 结果对象集合
*/
@Override
public List doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
//获取mybatis全局配置信息
Configuration configuration = ms.getConfiguration();
//构建一个RoutingStatementHandler对象
/**
* RountingStatementHandler:一个具体实现类,这个类中并没有对Statement对象具体使用,还是根据得到Excutor类型,
* 决定创建何种类型StatementHandler对象。在MyBatis工作时,使用的StatementHandler接口对象实际上就是RoutingStatementHandler对象
*/
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//构建Statement对象,并将参数值传入Statement对象中
Statement stmt = prepareStatement(handler, ms.getStatementLog());
//通知Statement将[select]推送到数据库并返回对应查询结果
return handler.query(stmt, resultHandler);
//并没有关闭statement
}
/**
* 执行查询SQL,返回游标对象
* @param ms Mapper.xml文件的select,delete,update,insert这些DML标签的封装类
* @param parameter 参数对象
* @param rowBounds Mybatis的分页对象
* @param boundSql 参数映射与可执行SQL封装类对象,已将参数对象加入到boundSql中
* @param 结果对象类型
* @return 存放结果对象的游标
*/
@Override
protected Cursor doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException {
//获取mybatis全局配置信息
Configuration configuration = ms.getConfiguration();
//构建一个RoutingStatementHandler对象
/**
* RountingStatementHandler:一个具体实现类,这个类中并没有对Statement对象具体使用,还是根据得到Excutor类型,
* 决定创建何种类型StatementHandler对象。在MyBatis工作时,使用的StatementHandler接口对象实际上就是RoutingStatementHandler对象
*/
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, null, boundSql);
//构建Statement对象,并将参数值传入Statement对象中
Statement stmt = prepareStatement(handler, ms.getStatementLog());
//通知stmt将[select]推送到数据库并返回对应游标结果
return handler.queryCursor(stmt);
//并没有关闭stmt
}
/**
* 如果不是回退,执行全部保存在执行器中的Statment对象,并将结果封装到BatchResult集合中;
* 否则不再执行全部保存在执行器中的Statment对象,直接返回空集合
* 作用于批处理
* 每当调用commit、rollback、close方法时,都会先调用该方法,然后再commit、rollback、close。
* @param isRollback 是否回退
* @return 空集合,mybatis
*/
@Override
public List doFlushStatements(boolean isRollback) {
//遍历Statement映射集合
for (Statement stmt : statementMap.values()) {
//关闭statement对象
closeStatement(stmt);
}
//清空Statement映射结合
statementMap.clear();
//返回空集合,ResueExecutor并没有做批处理,要想有批处理效果,请应用BatchExecutor
return Collections.emptyList();
}
/**
* 构建Statement对象,并将参数值传入Statement对象中
* @param handler Statement处理器
* @param statementLog MappedStatement对象的日志属性对象
* @return Statement对象
*/
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
//获取参数映射与可执行SQL封装类对象
BoundSql boundSql = handler.getBoundSql();
//获取可执行SQL
String sql = boundSql.getSql();
//如果 sql 存在Statement映射集合中,而且 sql 对应的Statement对象没有关闭
if (hasStatementFor(sql)) {
// 从statement映射集合中获取sql 对应的Statement对象
stmt = getStatement(sql);
///应用事务超时。事务超时大于查询时间,则不做更改;否则将查询时间修改成事务时间
applyTransactionTimeout(stmt);
} else {
//获取数据库连接对象
Connection connection = getConnection(statementLog);
//构建Statement对象,并设置事务超时时长
stmt = handler.prepare(connection, transaction.getTimeout());
//将 sql 和 stmt 加入到Statement映射集合中
putStatement(sql, stmt);
}
//将参数值传入stmt
handler.parameterize(stmt);
return stmt;
}
/**
* 判断{@code sql} 是否存在Statement映射集合中,而且 {@code sql} 对应的Statement对象是否没有关闭
* @param sql 可执行SQL脚本
* @return 如果从statement映射集合的键名集里存在sql,而且sql对应的Statement对象并没有关闭,返回true;否则,返回flase
*/
private boolean hasStatementFor(String sql) {
try {
//如果从statement映射集合的键名集里存在sql,而且sql对应的Statement对象并没有关闭,返回true;否则,返回flase
return statementMap.keySet().contains(sql) && !statementMap.get(sql).getConnection().isClosed();
} catch (SQLException e) {
//抛出SQLException时返回false
return false;
}
}
/**
* 从statement映射集合中获取 {@code s} 对应的Statement对象
* @param s 可执行SQL字符串,statement映射集合的键
* @return {@code s} 对应的Statement对象
*/
private Statement getStatement(String s) {
return statementMap.get(s);
}
/**
* 将 {@code sql} 和 {@code stmt} 加入到Statement映射集合中
* @param sql 可执行SQL脚本字符串
* @param stmt 执行 {@code sql} 的Statement对象
*/
private void putStatement(String sql, Statement stmt) {
statementMap.put(sql, stmt);
}
}