Mybatis-Executor源码解析

Mybatis3.5.1源码分析

  1. Mybatis-SqlSessionFactoryBuilder,XMLConfigBuilder,XPathParser源码解析
  2. Mybatis-Configuration源码解析
  3. Mybatis-事务对象源码解析
  4. Mybatis-数据源源码解析
  5. Mybatis缓存策略源码解析
  6. Mybatis-DatabaseIdProvider源码解析
  7. Mybatis-TypeHandler源码解析
  8. Mybatis-Reflector源码解析
  9. Mybatis-ObjectFactory,ObjectWrapperFactory源码分析
  10. Mybatis-Mapper各类标签封装类源码解析
  11. Mybatis-XMLMapperBuilder,XMLStatmentBuilder源码分析
  12. Mybatis-MapperAnnotationBuilder源码分析
  13. [Mybatis-MetaObject,MetaClass源码解析]//www.greatytc.com/p/f51fa552f30a)
  14. Mybatis-LanguageDriver源码解析
  15. Mybatis-SqlSource源码解析
  16. Mybatis-SqlNode源码解析
  17. Mybatis-KeyGenerator源码解析
  18. Mybatis-Executor源码解析
  19. Mybatis-ParameterHandler源码解析
  20. Mybatis-StatementHandler源码解析
  21. Mybatis-DefaultResultSetHandler(一)源码解析
  22. Mybatis-DefaultResultSetHandler(二)源码解析
  23. Mybatis-ResultHandler,Cursor,RowBounds 源码分析
  24. Mybatis-MapperProxy源码解析
  25. Mybatis-SqlSession源码解析
  26. 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协调工作
 * <p>
 * 参考博客:
 * <ol>
 *     <li><a href='https://www.cnblogs.com/virgosnail/p/10067964.html'>https://www.cnblogs.com/virgosnail/p/10067964.html</a></li>
 *     <li><a href='//www.greatytc.com/p/7f6072227390'>//www.greatytc.com/p/7f6072227390</a></li>
 * </ol>
 * </p>
 * @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 <E> 返回类型
   */
  <E> List<E> 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 <E> 返回类型
   */
  <E> List<E> 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 <E> 返回类型
   */
  <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;

  /**
   * 刷新Statements
   * @return
   * @throws SQLException
   */
  List<BatchResult> 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;

  /**
   * 延期加载队列
   * <p>
   * ConcurrentLinkedQueue:
   * <ol>
   *     <li>一个基于链接节点的无界线程安全队列。此队列按照 FIFO(先进先出)原则对元素进行排序。队列的头部 是队列中时间最长的元素。队列的尾部 是队列中时间最短的元素。</li>
   *     <li>新的元素插入到队列的尾部,队列获取操作从队列头部获得元素。当多个线程共享访问一个公共 collection 时,ConcurrentLinkedQueue 是一个恰当的选择。此队列不允许使用 null 元素。</li>
   * </ol>
   * </p>
   */
  protected ConcurrentLinkedQueue<DeferredLoad> 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<BatchResult> flushStatements() throws SQLException {
    return flushStatements(false);
  }

  /**
   * 如果不是回退,执行全部保存在执行器中的Statment对象,并将结果封装到BatchResult集合中;
   * 否则不再执行全部保存在执行器中的Statment对象,直接返回空集合
   * <p>作用于批处理</p>
   * <p>每当调用commit、rollback、close方法时,都会先调用该方法,然后再commit、rollback、close。</p>
   * @param isRollBack 是否回退
   */
  public List<BatchResult> 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 <E> List<E> 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 <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.");
    }
    //如果配置了flushCacheRequired为true,则会在执行器执行之前就清空本地一级缓存,换句话说就是关闭一级缓存功能
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      //查询堆栈为0且需要清空缓存,则执行清空缓存
      clearLocalCache();
    }
    List<E> list;
    try {
      //查询堆栈 + 1
      queryStack++;
      //如果此次查询的resultHandler为null(默认为null),则尝试从本地缓存中获取已经缓存的的结果,否则为null
      list = resultHandler == null ? (List<E>) 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 <E> Cursor<E> 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<ParameterMapping> 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对象,直接返回空集合
   * <p>作用于批处理</p>
   * <p>每当调用commit、rollback、close方法时,都会先调用该方法,然后再commit、rollback、close。</p>
   * @param isRollback 是否回退
   */
  protected abstract List<BatchResult> 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 <E> List<E> 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 <E> Cursor<E> 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 <code>Statement</code>
   * @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 <E> 返回类型
   */
  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
      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;
    }

    /**
     * 是否可以加载
     * <p>
     *     从 {@link #localCache} 中获取 {@link #key} 不为null且获取出来的值不是 {@link ExecutionPlaceholder#EXECUTION_PLACEHOLDER} (占位符) 就认为是可加载的。
     * </p>
     * @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<Object> list = (List<Object>) localCache.getObject(key);
      Object value = resultExtractor.extractObjectFromList(list, targetType);
      resultObject.setValue(property, value);
    }

  }

}

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 <E> 结果对象
   * @return 结果对象集合
   */
  @Override
  public <E> List<E> 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 <E> 结果对象类型
   * @return 存放结果对象的游标
   */
  @Override
  protected <E> Cursor<E> 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对象,直接返回空集合
   * <p>作用于批处理</p>
   * <p>每当调用commit、rollback、close方法时,都会先调用该方法,然后再commit、rollback、close。</p>
   * @param isRollback 是否回退
   */
  @Override
  public List<BatchResult> 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<Statement> statementList = new ArrayList<>();
  /**
   * 批处理结果对象集合
   */
  private final List<BatchResult> 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 <E> 结果对象类型
   * @return 结果对象集合
   */
  @Override
  public <E> List<E> 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 <E> 结果对象类型
   * @return 结果对象游标
   */
  @Override
  protected <E> Cursor<E> 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对象,直接返回空集合
   * <p>作用于批处理</p>
   * <p>每当调用commit、rollback、close方法时,都会先调用该方法,然后再commit、rollback、close。</p>
   * @param isRollback 是否回退
   * @return 批处理结果对象集合
   */
  @Override
  public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
    try {
      //创建批量结果对象集合
      List<BatchResult> 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<Object> 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;

/**
 * <p>
 *     作用于重复使用执行,其定义了一个Map<String, Statement>,将执行的sql作为key,
 *     将执行的Statement作为value保存,这样执行相同的sql时就可以使用已经存在的Statement,就不需要新创建了
 * </p>
 * <p>
 *     在执行commit,rollback,close,flushStatements方法时,才会对这个缓存Map进行遍历元素逐一关闭,
 *     然后清空缓存Map里的元素
 * </p>
 * @author Clinton Begin
 */
public class ReuseExecutor extends BaseExecutor {

  /**
   * 存储SQL与Statement对象关系映射的映射集合,这里直接简称为statement映射集合
   */
  private final Map<String, Statement> 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 <E> 结果对象类型
   * @return 结果对象集合
   */
  @Override
  public <E> List<E> 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 <E> 结果对象类型
   * @return 存放结果对象的游标
   */
  @Override
  protected <E> Cursor<E> 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对象,直接返回空集合
   * <p>作用于批处理</p>
   * <p>每当调用commit、rollback、close方法时,都会先调用该方法,然后再commit、rollback、close。</p>
   * @param isRollback 是否回退
   * @return 空集合,mybatis
   */
  @Override
  public List<BatchResult> 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);
  }

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容