Mybatis源码学习(四)——Executor

一 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();
      }
    }
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值