Mybatis之Executor简介

版权声明:转载前请留言获得作者许可,转载后标明作者Roger_CoderLife与原文链接。创作不易,感谢您的支持! https://blog.csdn.net/Roger_CoderLife/article/details/88397087

mybatis在执行期间,主要有四大核心接口对象:

  • 执行器Executor,执行器负责整个SQL执行过程的总体控制。
  • 参数处理器ParameterHandler,参数处理器负责PreparedStatement入参的具体设置。
  • 语句处理器StatementHandler,语句处理器负责和JDBC层具体交互,包括prepare语句,执行语句,以及调用ParameterHandler.parameterize()设置参数。
  • 结果集处理器ResultSetHandler,结果处理器负责将JDBC查询结果映射到java对象。
     

执行器Executor 

什么是执行器?Mybatis中所有的Mapper语句的执行都是通过Executor进行的,Executor是Mybatis的一个核心接口,其定义如下。从其定义的接口方法我们可以看出,对应的增删改语句是通过Executor接口的update方法进行的,查询是通过query方法进行的。虽然Executor接口的实现类有BaseExecutor和CachingExecutor,而BaseExecutor的子类又有SimpleExecutor、ReuseExecutor和BatchExecutor,但BaseExecutor是一个抽象类,其只实现了一些公共的封装,而把真正的核心实现都通过方法抽象出来给子类实现,如doUpdate()、doQuery();CachingExecutor只是在Executor的基础上加入了缓存的功能,底层还是通过Executor调用的,所以真正有作用的Executor只有SimpleExecutor、ReuseExecutor和BatchExecutor。它们都是自己实现的Executor核心功能,没有借助任何其它的Executor实现,它们是实现不同也就注定了它们的功能也是不一样的。Executor是跟SqlSession绑定在一起的,每一个SqlSession都拥有一个新的Executor对象,由Configuration创建。

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;

  List<BatchResult> flushStatements() throws SQLException;

  void commit(booleanrequired) throws SQLException;

  void rollback(booleanrequired) 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(booleanforceRollback);

  boolean isClosed();

  void setExecutorWrapper(Executor executor);

}

 下面是BaseExecutor的源码,我们可以看看它是怎么实现Executor接口的,是怎么开放接口给子类实现的。

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 = 0;

  private boolean closed;

 

  protected BaseExecutor(Configuration configuration, Transaction transaction) {

    this.transaction = transaction;

    this.deferredLoads = new ConcurrentLinkedQueue<DeferredLoad>();

    this.localCache = new PerpetualCache("LocalCache");

    this.localOutputParameterCache = new PerpetualCache("LocalOutputParameterCache");

    this.closed = false;

    this.configuration = configuration;

    this.wrapper = this;

  }

 

  @Override

  public Transaction getTransaction() {

    if (closed) {

      throw new ExecutorException("Executor was closed.");

    }

    returntransaction;

  }

 

  @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;

  }

 

  @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);

  }

 

  @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);

  }

 

  @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;

  }

 

  @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 {

      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();

    cacheKey.update(ms.getId());

    cacheKey.update(Integer.valueOf(rowBounds.getOffset()));

    cacheKey.update(Integer.valueOf(rowBounds.getLimit()));

    cacheKey.update(boundSql.getSql());

    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();

    TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();

    // mimic DefaultParameterHandler logic

    for (int i = 0; i < parameterMappings.size(); i++) {

      ParameterMapping parameterMapping = parameterMappings.get(i);

      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 {

          MetaObject metaObject = configuration.newMetaObject(parameterObject);

          value = metaObject.getValue(propertyName);

        }

        cacheKey.update(value);

      }

    }

    if (configuration.getEnvironment() != null) {

      // issue #176

      cacheKey.update(configuration.getEnvironment().getId());

    }

    return cacheKey;

  }   

 

  @Override

  public boolean isCached(MappedStatement ms, CacheKey key) {

    return localCache.getObject(key) != null;

  }

 

  @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();

    }

  }

 

  @Override

  public void rollback(boolean required) throws SQLException {

    if (!closed) {

      try {

        clearLocalCache();

        flushStatements(true);

      } finally {

        if (required) {

          transaction.rollback();

        }

      }

    }

  }

 

  @Override

  public void clearLocalCache() {

    if (!closed) {

      localCache.clear();

      localOutputParameterCache.clear();

    }

  }

 

  protected abstract int doUpdate(MappedStatement ms, Object parameter)

      throws SQLException;

 

  protected abstract List<BatchResult> doFlushStatements(boolean isRollback)

      throws SQLException;

 

  protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)

      throws SQLException;

 

  protected void closeStatement(Statement statement) {

    if (statement != null) {

      try {

        statement.close();

      } catch (SQLException e) {

        // ignore

      }

    }

  }

 

  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);

          }

        }

      }

    }

  }

 

  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);

    }

    returnlist;

  }

 

  protected Connection getConnection(Log statementLog) throws SQLException {

    Connection connection = transaction.getConnection();

    if (statementLog.isDebugEnabled()) {

      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;

    private final CacheKey key;

    private final PerpetualCache localCache;

    private final ObjectFactory objectFactory;

    private final ResultExtractor resultExtractor;

 

    // issue #781

    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;

    }

 

    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);

    }

 

  }

 

}

SimpleExecutor

SimpleExecutor是Mybatis执行Mapper语句时默认使用的Executor。它提供最基本的Mapper语句执行功能,没有过多的封装的。SimpleExecutor的源码如下。

public class SimpleExecutor extends BaseExecutor {

 

  public SimpleExecutor(Configuration configuration, Transaction transaction) {

    super(configuration, transaction);

  }

 

  @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());

      returnhandler.update(stmt);

    } finally {

      closeStatement(stmt);

    }

  }

 

  @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.<E>query(stmt, resultHandler);

    } finally {

      closeStatement(stmt);

    }

  }

 

  @Override

  public List<BatchResult> doFlushStatements(booleanisRollback) throws SQLException {

    return Collections.emptyList();

  }

 

  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {

    Statement stmt;

    Connection connection = getConnection(statementLog);

    stmt = handler.prepare(connection);

    handler.parameterize(stmt);

    returnstmt;

  }

 

}

5.2     ReuseExecutor

ReuseExecutor,顾名思义,是可以重用的Executor。它重用的是Statement对象,它会在内部利用一个Map把创建的Statement都缓存起来,每次在执行一条SQL语句时,它都会去判断之前是否存在基于该SQL缓存的Statement对象,存在而且之前缓存的Statement对象对应的Connection还没有关闭的时候就继续用之前的Statement对象,否则将创建一个新的Statement对象,并将其缓存起来。因为每一个新的SqlSession都有一个新的Executor对象,所以我们缓存在ReuseExecutor上的Statement的作用域是同一个SqlSession。以下是ReuseExecutor的源码。

public class ReuseExecutor extends BaseExecutor {

 

  private final Map<String, Statement> statementMap = new HashMap<String, Statement>();

 

  public ReuseExecutor(Configuration configuration, Transaction transaction) {

    super(configuration, transaction);

  }

 

  @Override

  public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {

    Configuration configuration = ms.getConfiguration();

    StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);

    Statement stmt = prepareStatement(handler, ms.getStatementLog());

    return handler.update(stmt);

  }

 

  @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.<E>query(stmt, resultHandler);

  }

 

  @Override

  public List<BatchResult> doFlushStatements(booleanisRollback) throws SQLException {

    for (Statement stmt : statementMap.values()) {

      closeStatement(stmt);

    }

    statementMap.clear();

    return Collections.emptyList();

  }

 

  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);

    } else {

      Connection connection = getConnection(statementLog);

      stmt = handler.prepare(connection);

      putStatement(sql, stmt);

    }

    handler.parameterize(stmt);

    return stmt;

  }

 

  private boolean hasStatementFor(String sql) {

    try {

      return statementMap.keySet().contains(sql) && !statementMap.get(sql).getConnection().isClosed();

    } catch (SQLException e) {

      return false;

    }

  }

 

  private Statement getStatement(String s) {

    return statementMap.get(s);

  }

 

  private void putStatement(String sql, Statement stmt) {

    statementMap.put(sql, stmt);

  }

 

}

 

BatchExecutor

BatchExecutor的设计主要是用于做批量更新操作的。其底层会调用Statement的executeBatch()方法实现批量操作。以下是BatchExecutor的源码。

public class BatchExecutor extends BaseExecutor {

  public static final int BATCH_UPDATE_RETURN_VALUE = Integer.MIN_VALUE + 1002;

  private final List<Statement> statementList = new ArrayList<Statement>();
  private final List<BatchResult> batchResultList = new ArrayList<BatchResult>();
  private String currentSql;
  private MappedStatement currentStatement;

  public BatchExecutor(Configuration configuration, Transaction transaction) {
    super(configuration, transaction);
  }

  @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);
     handler.parameterize(stmt);//fix Issues 322
      BatchResult batchResult = batchResultList.get(last);
      batchResult.addParameterObject(parameterObject);
    } else {
      Connection connection = getConnection(ms.getStatementLog());
      stmt = handler.prepare(connection);
      handler.parameterize(stmt);    //fix Issues 322
      currentSql = sql;
      currentStatement = ms;
      statementList.add(stmt);
      batchResultList.add(new BatchResult(ms, sql, parameterObject));
    }
  // handler.parameterize(stmt);
    handler.batch(stmt);
    return BATCH_UPDATE_RETURN_VALUE;
  }

  @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);
      handler.parameterize(stmt);
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

  @Override
  public List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException {
    try {
      List<BatchResult> results = new ArrayList<BatchResult>();
      if (isRollback) {
        return Collections.emptyList();
      }
      for (int i = 0, n = statementList.size(); i < n; i++) {
        Statement stmt = statementList.get(i);
        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);
            }
          }
        } 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

缓存执行器相对于其他执行器的差别在于,首先是在query()方法中判断是否使用二级缓存(也就是mapper级别的缓存)。虽然mybatis默认启用了CachingExecutor,但是如果在mapper层面没有明确设置二级缓存的话,就退化为SimpleExecutor了。二级缓存的维护由TransactionalCache(事务化缓存)负责,当在TransactionalCacheManager(事务化缓存管理器)中调用putObject和removeObject方法的时候并不是马上就把对象存放到缓存或者从缓存中删除,而是先把这个对象放到entriesToAddOnCommit和entriesToRemoveOnCommit这两个HashMap之中的一个里,然后当执行commit/rollback方法时再真正地把对象存放到缓存或者从缓存中删除,具体可以参见TransactionalCache.commit/rollback方法。
  还有一个差别是使用了TransactionalCacheManager管理事务,其他逻辑就一样了。
 

作者:Roger_CoderLife

链接:https://blog.csdn.net/Roger_CoderLife/article/details/88397087

本文为Roger_CoderLife的原创文章,著作权归作者所有,转载请注明原文出处,欢迎转载!

猜你喜欢

转载自blog.csdn.net/Roger_CoderLife/article/details/88397087