第五篇 mybatis源码阅读:Executor的设计

一、Executor执行器的作用

Mybatis的增删改查都是通过创建SqlSession对象完成,除了这写SqlSession还需要一些辅助功能,比如提交事务、数据回滚、关闭会话、清除缓存等。下面是DefaultSqlSession的源码,通过DefaultSqlSession的源码,可以发现SqlSession实际上都是将功能委托给Executor完成,Executor的作用就是代替SqlSession完成增删改查、事务提交、数据回滚、关闭执行器等功能,这样下来,Executor就变成了Mybatis的核心。

public class DefaultSqlSession implements SqlSession {
  private final Configuration configuration;
  private final Executor executor;
  private final boolean autoCommit;
  private boolean dirty;
  private List<Cursor<?>> cursorList;

  public DefaultSqlSession(Configuration configuration, Executor executor, boolean autoCommit) {
    this.configuration = configuration;
    this.executor = executor;
    this.dirty = false;
    this.autoCommit = autoCommit;
  }
  ...
  // 查询
  private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
    try {
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
  // 更新    
  @Override
  public int update(String statement, Object parameter) {
    try {
      dirty = true;
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.update(ms, wrapCollection(parameter));
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

  //提交事务
  @Override
  public void commit(boolean force) {
    try {
      executor.commit(isCommitOrRollbackRequired(force));
      dirty = false;
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error committing transaction.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
  // 回滚
  @Override
  public void rollback(boolean force) {
    try {
      executor.rollback(isCommitOrRollbackRequired(force));
      dirty = false;
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error rolling back transaction.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
  //当你将 ExecutorType 设置为 ExecutorType.BATCH 时,进行批处理刷新
  @Override
  public List<BatchResult> flushStatements() {
    try {
      return executor.flushStatements();
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error flushing statements.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
  //关闭会话
  @Override
  public void close() {
    try {
      executor.close(isCommitOrRollbackRequired(false));
      closeCursors();
      dirty = false;
    } finally {
      ErrorContext.instance().reset();
    }
  }
  //清理缓存
  @Override
  public void clearCache() {
    executor.clearLocalCache();
  }
  ...
}

二、Executor执行器的设计

再来看Executor执行器的设计,SqlSession委托Executor完成数据库操作,再结合上Executor的UML类图,可以知道整个设计如下图,图中Executor接口有CachingExecutor和BaseExecutor,而BaseExecutor又有四个子类实现,其中,ClosedExecutor只是实现了BaseExecutor的close方法,被在异常时调用来关闭会话,不多说,下面就是其它三个执行器的作用:

  • SimpleExecutor(简单执行器):默认执行器,每次执行SQL都会创建一个新的Statement,用完关闭。
  • ReuseExecutor(复用执行器): 内部维护一个Statement池,达到Statement的复用
  • BatchExecutor(批处理执行器): 批量执行SQL

可以看到图中,SqlSession先调用的CachingExecutor执行,而不是BaseExecutor,这个原因后面说,先来看看执行器创建的源码,Executor是在SqlSession创建是之前创建,调用configuration的newExecutor方法,在newExecutor方法中,当cacheEnabled是true,都是创建CachingExecutor,用CachingExecutor装饰BaseExecutor,所以SqlSession中持有的Executor是被CachingExecutor,而SqlSession对象,都是Mapper代理类调用,所以SqlSession进入的第一个执行器是CachingExecutor。

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
      //开启一个事务
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      //DefaultSqlSession持有一个Executor对象,所以先创建一个执行器
      final Executor executor = configuration.newExecutor(tx, execType);
      //创建一个默认的SqlSession
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    if (ExecutorType.BATCH == executorType) {
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {
      executor = new ReuseExecutor(this, transaction);
    } else {
        //默认创建SimpleExecutor
      executor = new SimpleExecutor(this, transaction);
    }
    // 默认开启一级缓存(cacheEnabled是全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。默认true)
    if (cacheEnabled) {
      executor = new CachingExecutor(executor); //装饰器模式 CachingExecutor装饰executor
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;
  }
  

再来看CachingExecutor的源码,CachingExecutor继承于Executor接口,以query方法为例,可以发现在CachingExecutor实际只是处理了二级缓存的部分,更新和查询的逻辑都是交给持有的BaseExecutor处理。

public class CachingExecutor implements Executor {

  private final Executor delegate;  // 持有的BaseExecutor执行器
  private final TransactionalCacheManager tcm = new TransactionalCacheManager();  
  
  ...
  
  @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    //拼接的SQL
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    //缓存key, 由MappedStatement的Id,参数,分页参数,sql组成
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

  @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);
        //获取二级缓存
        List<E> list = (List<E>) tcm.getObject(cache, key);
        if (list == null) {
          //缓存为null,委托给BaseExecutor执行查询
          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);
    }
}

进入到BaseExecutor, 会使用BaseExecutor的默认实现SimpleExecutor,CachingExecutor首先会进入BaseExecutor的query方法,在BaseExecutor中会处理一级缓存,一级缓存是会话级缓存,默认开启,缓存的key和二级缓存相同,如果没有命中一级缓存,就会调用doQuery方法从数据库中查询,BaseExecutor并没有实现该方法,交由子类实现,BaseExecutor还有一个作用就是获取数据库连接。

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;
  ...
    @Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameter);
    // 缓存key,这里是一级缓存key
    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.");
    }
    //如果查询堆栈是0(就是之前没有执行过)且Mapper的flushCacheRequired是true
    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;
  }
  ...
    private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, 
CacheKey key, BoundSql boundSql) throws SQLException { List<E> list; // 先占位一个key, localCache.putObject(key, EXECUTION_PLACEHOLDER); try { //查询数据库 list = doQuery(ms, parameter, rowBounds, resultHandler, boundSql); } finally { // 异常删除key localCache.removeObject(key); } // 放入缓存 localCache.putObject(key, list); if (ms.getStatementType() == StatementType.CALLABLE) { localOutputParameterCache.putObject(key, parameter); } return list; } ... //获取数据连接 protected Connection getConnection(Log statementLog) throws SQLException { Connection connection = transaction.getConnection(); if (statementLog.isDebugEnabled()) { return ConnectionLogger.newInstance(connection, statementLog, queryStack); } else { return connection; } } }

BaseExcutor的子类实现有三个,SimpleExecutor是默认执行器,SimpleExecutor处理查询时每次都会创建一个Statement,用完之后便会关闭,Statement只能使用一次,即使是同一个SQL。

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);
      // 预编译创建Statement
      stmt = prepareStatement(handler, ms.getStatementLog());
      //执行查询
      return handler.query(stmt, resultHandler);
    } finally {
        //关闭连接
      closeStatement(stmt);
    }
  }

  // 预编译SQL,返回prepareStatement
  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;
  }
    ...
}

ReuseExecutor(复用执行器)与SimpleExecutor不同的是,ReuseExecutor内部维护了一个HashMap的statement池,sql为key,Statement为value,所以当同一个sql会使用同一个Statement,因为HashMap并不是线程安全,所以仅限于单线程使用ReuseExecutor执行器。

public class ReuseExecutor extends BaseExecutor {

  private final Map<String, Statement> statementMap = new HashMap<>();
    ...
    @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);
  }
    ...
  private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
    Statement stmt;
    BoundSql boundSql = handler.getBoundSql();
    String sql = boundSql.getSql();
    // 判断是否存在该sql的Statement
    if (hasStatementFor(sql)) {
      stmt = getStatement(sql);
      applyTransactionTimeout(stmt);
    } else {
      // 没有创建Statement,并放入Statement池中
      Connection connection = getConnection(statementLog);
      stmt = handler.prepare(connection, transaction.getTimeout());
      putStatement(sql, stmt);
    }
    handler.parameterize(stmt);
    return stmt;
  }
  ...
}

BatchExecutor(批处理执行器)是用来做批量执行SQL的,执行器中维护了一个statementList,BatchExecutor会首先将sql的预编译的Statement都放入statementList中,批处理主要用于update方法,所以关键代码为doUpdate方法,doUpdate方法中并没有执行,而是doFlushStatements方法被调用时执行。

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;
   
    @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;
      //添加Statement
      statementList.add(stmt);
      batchResultList.add(new BatchResult(ms, sql, parameterObject));
    }
    handler.batch(stmt);
    return BATCH_UPDATE_RETURN_VALUE;
  }
  
  @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();
    }
  }

最后用一张图总结执行器

posted @ 2019-08-14 16:42  哲雪君!  阅读(693)  评论(0编辑  收藏  举报