Mybatis源码解析-3通过SqlSession进行查询操作

1.查询操作

SqlSession中定义的查询有以下这些:

  <T> T selectOne(String statement);

  <T> T selectOne(String statement, Object parameter);

  <E> List<E> selectList(String statement);

  <E> List<E> selectList(String statement, Object parameter);

  <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds);

  <K, V> Map<K, V> selectMap(String statement, String mapKey);

  <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey);

  <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds);

  <T> Cursor<T> selectCursor(String statement);

  <T> Cursor<T> selectCursor(String statement, Object parameter);

  <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds);

  void select(String statement, Object parameter, ResultHandler handler);

  void select(String statement, ResultHandler handler);

  void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler);

其中最关键的方法就是selectList,观察其方法实现,其实不管是selectOne还是selectMap,其方法都会调用selectList方法进行查询数据,如下:

  /**
   * 查询一个
   */
  @Override
  public <T> T selectOne(String statement, Object parameter) {
    //调用selectList方法查询数据
    List<T> list = this.selectList(statement, parameter);
    if (list.size() == 1) {	//若只有一个数据
      return list.get(0);	//则返回该数据
    } else if (list.size() > 1) {	//若大于一个数据则抛出异常
      throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
    } else {	//没有数据返回null
      return null;
    }
  }


  @Override
  public <K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowBounds) {
	//调用selectList进行查询
    final List<? extends V> list = selectList(statement, parameter, rowBounds);
    
    //后面是组装结果
    final DefaultMapResultHandler<K, V> mapResultHandler = new DefaultMapResultHandler<>(mapKey,
            configuration.getObjectFactory(), configuration.getObjectWrapperFactory(), configuration.getReflectorFactory());
    final DefaultResultContext<V> context = new DefaultResultContext<>();
    for (V o : list) {
      context.nextResultObject(o);
      mapResultHandler.handleResult(context);
    }
    return mapResultHandler.getMappedResults();
  }

2. selectList解析

selectList方法的源码如下,我们这里查看的是默认的SqlSession,也就是DefaultSqlSession:

  /**
   * 查询list
   */
  @Override
  public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
    try {
      //根据statement获取对应的MappedStatement
      MappedStatement ms = configuration.getMappedStatement(statement);
      //调用执行器查询数据并返回
      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();
    }
  }

其中MappedStatement是我们在第一步读取配置文件时,根据配置生成的,是根据Mapper文件中的标签生成的,最后放到configuration中,这里就是根据id从configuration中获取到对应的MappedStatement,这里会直接调用执行器executor的query进行查询并返回,这里再看一眼执行器的生成(上一节内容):

  /**
   * 创建一个执行器
   * @param transaction	事务
   * @param executorType 执行器类型
   */
  public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
	//若executorType为null 则设置成defaultExecutorType也就是ExecutorType.SIMPLE
    executorType = executorType == null ? defaultExecutorType : executorType;
    executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
    Executor executor;
    //根据类型生成对应的执行器对象
    if (ExecutorType.BATCH == executorType) {	//BATCH类型
      executor = new BatchExecutor(this, transaction);
    } else if (ExecutorType.REUSE == executorType) {	//REUSE类型
      executor = new ReuseExecutor(this, transaction);
    } else {	//默认的SIMPLE类型
      executor = new SimpleExecutor(this, transaction);
    }
    if (cacheEnabled) {	//是否开启缓存
      //通过CachingExecutor对生成的执行器executor进行包装
      executor = new CachingExecutor(executor);
    }
    executor = (Executor) interceptorChain.pluginAll(executor);
    return executor;	//返回执行器
  }

根据执行器生成执行器后,会检查是否开启二级缓存,若开启,则会将执行器包装成CachingExecutor类型,这里我们也直接重CachingExecutor类中继续查看。

  /**
   * 查询数据
   */
  @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    //根据参数以及MappedStatement获取对应BoundSql
	BoundSql boundSql = ms.getBoundSql(parameterObject);
	//生成CacheKey
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    //调用query方法查询数据
    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);
        //直接从缓存中获取数据
        @SuppressWarnings("unchecked")
        List<E> list = (List<E>) tcm.getObject(cache, key);
        //若缓存中未找到对应数据
        if (list == null) {
          //调用delegate的query方法进行查询
          list = delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
          //将查询结果加入缓存中
          tcm.putObject(cache, key, list); // issue #578 and #116
        }
        return list;
      }
    }
    //缓存不存在或未开启二级缓存,这里会直接调用delegate的query方法进行查询数据并将结果返回
    return delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

这里我们可以看到CachingExecutor并未直接进行查询,而是先检查二级缓存中是否有对应数据,如果有,直接返回,若是没有,则调用属性delegate的方法进行查询,并将结果缓存起来,若是未开启缓存,或未开启二级缓存,这里就直接属性delegate的方法进行查询,CachingExecutor本身只是进行二级缓存相关逻辑,下面我们继续看以下delegate中的query方法,这里delegate我们默认的是SimpleExecutor执行器,其query方法是在父类BaseExecutor中定义的,代码如下:

  /**
   * 查询方法
   */
  @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;
  }

这里还是有一个缓存处理的逻辑,这里是一级缓存处理的逻辑,跟二级缓存处理类似,缓存中找不到的时候会调用queryFromDatabase方法从数据库中获取数据,我们看一下这个方法:

  /**
   * queryFromDatabase从数据库中查询数据
   */
  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;
  }

这里有一个查询到数据加入到缓存中,具体的查询方法是调用的doQuery方法,这个方法在BaseExecutor中并未实现,是在具体的执行器中实现的,我们去看一下SimpleExecutor中的doQuery方法

  /**
   * 查询数据doQuery
   */
  @Override
  public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      //获取Configuration
      Configuration configuration = ms.getConfiguration();
      //创建一个StatementHandler,这里是创建一个RoutingStatementHandler的实例
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      //生成Statement
      stmt = prepareStatement(handler, ms.getStatementLog());
      //调用handler的query方法进行查询
      return handler.query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

RoutingStatementHandler构造方法如下

  public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

	//根据StatementType生成对应的StatementHandler赋值给delegate属性
    switch (ms.getStatementType()) {
      case STATEMENT:
        delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case PREPARED:
        delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      case CALLABLE:
        delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
        break;
      default:
        throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
    }

  }

这个类似与CachingExecutor与具体执行器的关系,这里我们以SimpleStatementHandler为例,看一下其查询方法:

  /**
   * 查询
   */
  @Override
  public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
	//获取sql
    String sql = boundSql.getSql();
    //执行sql
    statement.execute(sql);
    //结果处理
    return resultSetHandler.handleResultSets(statement);
  }

这就是查询数据整个的过程。。。

猜你喜欢

转载自blog.csdn.net/luo_mu_hpu/article/details/108058947
今日推荐