SQL执行流程(二)——Executor执行SELECT查询语句过程

563 阅读5分钟

前言

通过 MapperMethod#execute() 方法,可以得知执行 SELECT 查询语句的方法比较多:

  • executeWithResultHandler
  • executeForMany
  • executeForMap
  • executeForCursor

这些方法在内部调用了SqlSession的一些 select* 方法。这些方法的返回值类型是不同的,因此对于每种返回类型,需要有专门的处理方法。以 selectList() 方法为例,该方法的返回值类型为 List。但如果 MapperDao 的接口方法返回值类型为数组,或者 Set,直接将 List 类型的结果返回给 Mapper/Dao 就不合 适了。execute* 等方法只是对 select* 等方法做了一层简单的封装。

selectOne

SqlSession#selectOne() 方法内部会调用 selectList()

// SqlSession
public <T> T selectOne(String statement, Object parameter) {
  List<T> list = this.selectList(statement, parameter);
  if (list.size() == 1) {
    return list.get(0);
  } else if (list.size() > 1) {
    // 常见的异常。如果查询结果大于1则会抛出
    throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
  } else {
    return null;
  }
}

selectList

查询职责最终会委托给 Executor#query() 方法。

public <E> List<E> selectList(String statement) {
  // 调用重载方法
  return this.selectList(statement, null);
}

public <E> List<E> selectList(String statement, Object parameter) {
  // 调用重载方法
  return this.selectList(statement, parameter, RowBounds.DEFAULT);
}

// selectList最终实现方法
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
  try {
    // 获取 MappedStatement
    MappedStatement ms = configuration.getMappedStatement(statement);
    // 调用Executor实现类的query()方法
    return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
  } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
  } finally {
    ErrorContext.instance().reset();
  }
}

Executor

Executor 是个接口,用于执行定义的SQL语句。其实现类关系图:

image.png

创建默认Executor

executorDefaultSqlSession 中的成员变量。跟踪下 DefaultSqlSessionFactory#openSession() 方法,默认是创建 CachingExecutor 实例对象。 CachingExecutor 类是个装饰器类,用于给目标Executor增加二级缓存功能。目标Executor默认是 SimpleExecutor

// DefaultSqlSessionFactory
public SqlSession openSession() {
  // ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
  // Configuration对象中的默认executor type为SIMPLE
  // 创建默认的 SimpleExecutor 对象
  return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}

// DefaultSqlSessionFactory
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);
    // 根据参数创建指定的 Executor 实例对象
    final Executor executor = configuration.newExecutor(tx, execType);
    // 返回 DefaultSqlSession 实例对象
    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();
  }
}

// Configuration
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);
  }
  
  // 默认为true
  if (cacheEnabled) {
    // 装饰器类,用于给目标Executor增加二级缓存功能
    executor = new CachingExecutor(executor);
  }
  executor = (Executor) interceptorChain.pluginAll(executor);
  return executor;
}

CachingExecutor#query

SqlSession#selectList() 方法的查询功能,最终会委托给 CachingExecutor#query() 方法。CachingExecutor 类实际作用就是 增加二级缓存功能

// CachingExecutor
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
  // 获取BoundSql
  BoundSql boundSql = ms.getBoundSql(parameterObject);
  // 创建CacheKey
  CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
  // 调用重载方法
  return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

// CachingExecutor
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
    throws SQLException {
  // 从MappedStatement 中获取缓存
  Cache cache = ms.getCache();
  // XML映射文件中未配置缓存,则cache = null
  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) {
        // 若缓存未命中,则调用被装饰器类的 query() 方法
        list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
        tcm.putObject(cache, key, list); 
      }
      return list;
    }
  }
  // 调用被装饰器类的 query() 方法
  return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}

最终执行查询语句的功能还是会委托给目标Executor,这就是装饰器设计模式。这里调用的 query() 方法,实际上是目标Executor的父类BaseExecutorquery() 方法。

BaseExecutor#query

父类的 query() 方法,是完成通用功能,从一级缓存中查询结果集,如果缓存未命中,则调用 queryFromDatabase() 方法从数据库查询。

// BaseExecutor
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();
    }
    
    deferredLoads.clear();
    if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
      clearLocalCache();
    }
  }
  return list;
}

BaseExecutor#queryFromDatabase

queryFromDatabase() 方法最终还是会调用 doQuery() 方法进行查询,并将返回的结果集添加到一级缓存中。但是 doQuery() 方法在 BaseExecutor 类中是个抽象方法,所以最终的查询功能还是由不同的子类去实现。

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 {
    // 调用 doQuery() 进行查询
    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;
}

SimpleExecutor#doQuery

到这里整个调用过程就比较清晰了,可以看出整个查询功能是由 StatementHandler 类完成的。而且已经清楚 prepareStatement() 方法创建了 Statment 对象。这已经是属于 JDBC 的范畴了。

// SimpleExecutor
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
    StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
    // 创建Statement
    stmt = prepareStatement(handler, ms.getStatementLog());
    // 执行查询操作
    return handler.query(stmt, resultHandler);
  } finally {
    // 关闭Statement
    closeStatement(stmt);
  }
}

StatementHandler 是个接口,其中主要有三个实现类SimpleStatementHandlerPreparedStatementHandlerCallableStatementHandler 分别对应 statementTypeSTATEMENTPREPAREDCALLABLE

statementType | 可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。

PreparedStatementHandler#query

到这里整个调用过程算是已经结束了。此方法就是执行sql,并将结果集进行处理。

public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  PreparedStatement ps = (PreparedStatement) statement;
  // 执行sql
  ps.execute();
  // 处理执行结果
  return resultSetHandler.handleResultSets(ps);
}