Mybatis学习系列(九):select的执行

本文深入探讨了Mybatis中的select查询实现,包括返回void、集合、Map、Cursor及单一对象的情况。详细分析了sqlsession的select方法、缓存处理、数据库查询、SimpleExecutor的doQuery方法以及resultSetHandler如何解析结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们先看一下MapperMethod中有哪些select查询,都是如何实现的

case SELECT:
        //1.返回void
        if (method.returnsVoid() && method.hasResultHandler()) {
          executeWithResultHandler(sqlSession, args);
          result = null;
        } else if (method.returnsMany()) {
          //2.返回集合
          result = executeForMany(sqlSession, args);
        } else if (method.returnsMap()) {
          //3.返回map
          result = executeForMap(sqlSession, args);
        } else if (method.returnsCursor()) {
          //4.返回游标
          result = executeForCursor(sqlSession, args);
        } else {
          //5.返回单个对象
          Object param = method.convertArgsToSqlCommandParam(args);
          result = sqlSession.selectOne(command.getName(), param);
        }

1.返回void

private void executeWithResultHandler(SqlSession sqlSession, Object[] args) {
    //获取MappedStatement 
    MappedStatement ms = sqlSession.getConfiguration().getMappedStatement(command.getName());
    if (!StatementType.CALLABLE.equals(ms.getStatementType())
        && void.class.equals(ms.getResultMaps().get(0).getType())) {
      throw new BindingException("method " + command.getName()
          + " needs either a @ResultMap annotation, a @ResultType annotation,"
          + " or a resultType attribute in XML so a ResultHandler can be used as a parameter.");
    }
    //获取所有的入参
    Object param = method.convertArgsToSqlCommandParam(args);
    //是否包含分页信息
    //调用sqlSession的select方法
    if (method.hasRowBounds()) {
      RowBounds rowBounds = method.extractRowBounds(args);
      sqlSession.select(command.getName(), param, rowBounds, method.extractResultHandler(args));
    } else {
      sqlSession.select(command.getName(), param, method.extractResultHandler(args));
    }
  }

我们看下sqlsession的select方法:

  public void select(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
    try {
      //获取MappedStatement 
      MappedStatement ms = configuration.getMappedStatement(statement);
      //执行executor的query方法
      executor.query(ms, wrapCollection(parameter), rowBounds, handler);
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

这里其实是调用了BaseExecuto的query方法:

public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    //获取执行的sql信息
    BoundSql boundSql = ms.getBoundSql(parameter);
    //获取当前执行方法的缓存key
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    //执行方法
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
 }


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

我们先看一下取缓存数据后的对参数的操作:

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);
    }
    return list;
  }

这里doquery其实是SimpleExecutor的方法:

public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
    Statement stmt = null;
    try {
      //1.获取配置信息
      Configuration configuration = ms.getConfiguration();
      //2.获取StatementHandler 并初始化数据
      StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
      //3.获取Statement 对象
      stmt = prepareStatement(handler, ms.getStatementLog());
      //4.执行StatementHandler 的query方法
      return handler.<E>query(stmt, resultHandler);
    } finally {
      closeStatement(stmt);
    }
  }

我们可以看到这里的1,2,3步跟前面的insert,update,delete的操作是一致的,不做解析,我们主要看第4步的查询方法:

 public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    //执行PreparedStatement 的execute方法
    ps.execute();
    //解析返回的数据
    return resultSetHandler.<E> handleResultSets(ps);
  }

这里我们主要看返回数据的解析,这里调用的是resultSetHandler对象的handleResultSets的方法:这里的resultSetHandler也是在获取statementHandler的时候初始化的:调用的是configuration的方法

this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, rowBounds, parameterHandler, resultHandler, boundSql);

public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
      ResultHandler resultHandler, BoundSql boundSql) {
    //初始化一个DefaultResultSetHandler
    ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
    //在DefaultResultSetHandler上添加插件拦截器链
    resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
    return resultSetHandler;
  }

下面我们看一下具体解析返回的参数方法:

public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

    final List<Object> multipleResults = new ArrayList<Object>();

    int resultSetCount = 0;
    //获取第一条数据
    ResultSetWrapper rsw = getFirstResultSet(stmt);
    //获取当前方法的ResultMap数据
    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    //获取ResultMap个数,一般都是一个
    int resultMapCount = resultMaps.size();
    //校验查询数据是否有效
    validateResultMapsCount(rsw, resultMapCount);
    //循环解析每一个ResultSet  也就是必须设定了返回的ResultMap 
    //如果设定不是ResultMap 走后面的resultSets逻辑
    while (rsw != null && resultMapCount > resultSetCount) {
      //获取当前对应的ResultMap 数据  也就是返回字段的定义
      ResultMap resultMap = resultMaps.get(resultSetCount);
      //解析转换赋值
      handleResultSet(rsw, resultMap, multipleResults, null);
      //获取下一个ResultSet进行后续解析
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }
    //这里判断是不是设置的resultType
    String[] resultSets = mappedStatement.getResultSets();
    if (resultSets != null) {
      //循环解析每个resultSet
      while (rsw != null && resultSetCount < resultSets.length) {
        ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
        if (parentMapping != null) {
          String nestedResultMapId = parentMapping.getNestedResultMapId();
          ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
          handleResultSet(rsw, resultMap, null, parentMapping);
        }
        //获取下一个resultSet
        rsw = getNextResultSet(stmt);
        cleanUpAfterHandlingResultSet();
        resultSetCount++;
      }
    }
    //返回解析后的数据
    return collapseSingleResultList(multipleResults);
  }

我们看一下每一个ResultSet数据的解析:

private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
    try {
      if (parentMapping != null) {
        //解析ResultSet数据
        handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
      } else {
        if (resultHandler == null) {
          DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
         //解析ResultSet数据
          handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
          //只有这种情况返回的是我们自定义的list 一般都是走的这里
          //把上一步的defaultResultHandler的list添加到multipleResults中
          multipleResults.add(defaultResultHandler.getResultList());
        } else {
          //解析ResultSet数据
          handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
        }
      }
    } finally {
      // issue #228 (close resultsets)
      closeResultSet(rsw.getResultSet());
    }
  }


//解析ResultSet数据
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
    //ResultMap有子集关系的
    if (resultMap.hasNestedResultMaps()) {
      ensureNoRowBounds();
      checkResultHandler();
      handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    } else {
      //普通单一ResultMap
      handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    }
  }


//普通单一ResultMap
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
      throws SQLException {
    DefaultResultContext<Object> resultContext = new DefaultResultContext<Object>();
    //如果有分页参数 跳过前面的数据
    skipRows(rsw.getResultSet(), rowBounds);
    //循环解析每行数据
    while (shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {
      ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, null);
      //获取行数据
      Object rowValue = getRowValue(rsw, discriminatedResultMap);
      //这里把行数据设置到resultContext里面
      storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());
    }
  }

//这里把行数据设置到resultContext里面
private void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException {
    if (parentMapping != null) {
      linkToParents(rs, parentMapping, rowValue);
    } else {
      //rowValue设置到resultContext
      callResultHandler(resultHandler, resultContext, rowValue);
    }
  }

//rowValue设置到resultContext
private void callResultHandler(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue) {
    //把rowValue设置给resultContext的resultObject
    resultContext.nextResultObject(rowValue);
    把当前resultContext的resultObject设置到DefaultResultHandler的list中
    ((ResultHandler<Object>) resultHandler).handleResult(resultContext);
  }

2.返回集合

private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
    List<E> result;
    //获取入参
    Object param = method.convertArgsToSqlCommandParam(args);
    //有没有分页信息
    if (method.hasRowBounds()) {
      RowBounds rowBounds = method.extractRowBounds(args);
      //调用sqlSession.selectList方法 最终都是走的query方法,跟返回void一致不在说明
      result = sqlSession.<E>selectList(command.getName(), param, rowBounds);
    } else {
      //调用sqlSession.selectList方法,跟返回void一致不在说明
      result = sqlSession.<E>selectList(command.getName(), param);
    }
    // issue #510 Collections & arrays support
    if (!method.getReturnType().isAssignableFrom(result.getClass())) {
      //返回数据是数组
      if (method.getReturnType().isArray()) {
        return convertToArray(result);
      } else {
        //集合
        return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
      }
    }
    return result;
  }

//返回数据是数组
private <E> Object convertToArray(List<E> list) {
    Class<?> arrayComponentType = method.getReturnType().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));
      }
      return array;
    } else {
      return list.toArray((E[])array);
    }
  }

//集合
 private <E> Object convertToDeclaredCollection(Configuration config, List<E> list) {
    Object collection = config.getObjectFactory().create(method.getReturnType());
    MetaObject metaObject = config.newMetaObject(collection);
    metaObject.addAll(list);
    return collection;
  }

3.返回map

private <K, V> Map<K, V> executeForMap(SqlSession sqlSession, Object[] args) {
    Map<K, V> result;
    //获取入参
    Object param = method.convertArgsToSqlCommandParam(args);
    if (method.hasRowBounds()) {
      RowBounds rowBounds = method.extractRowBounds(args);
      //调用sqlSession.selectMap方法
      result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey(), rowBounds);
    } else {
      result = sqlSession.<K, V>selectMap(command.getName(), param, method.getMapKey());
    }
    return result;
  }

//调用sqlSession.selectMap方法
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);
    //这里把数据转换成map格式
    final DefaultMapResultHandler<K, V> mapResultHandler = new DefaultMapResultHandler<K, V>(mapKey,
        configuration.getObjectFactory(), configuration.getObjectWrapperFactory(), configuration.getReflectorFactory());
    final DefaultResultContext<V> context = new DefaultResultContext<V>();
    for (V o : list) {
      context.nextResultObject(o);
      mapResultHandler.handleResult(context);
    }
    return mapResultHandler.getMappedResults();
  }

4.返回Cursor

private <T> Cursor<T> executeForCursor(SqlSession sqlSession, Object[] args) {
    Cursor<T> result;
    //获取入参
    Object param = method.convertArgsToSqlCommandParam(args);
    if (method.hasRowBounds()) {
      RowBounds rowBounds = method.extractRowBounds(args);
      //调用sqlSession.selectCursor方法
      result = sqlSession.<T>selectCursor(command.getName(), param, rowBounds);
    } else {
      //调用sqlSession.selectCursor方法
      result = sqlSession.<T>selectCursor(command.getName(), param);
    }
    return result;
  }

//调用sqlSession.selectCursor方法
public <T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds) {
    try {
      //获取MappedStatement 
      MappedStatement ms = configuration.getMappedStatement(statement);
      //调用executor.queryCursor方法
      Cursor<T> cursor = executor.queryCursor(ms, wrapCollection(parameter), rowBounds);
      //把当前cursor添加到cursorList中
      registerCursor(cursor);
      return cursor;
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

//调用executor.queryCursor方法
public <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException {
    //获取执行SQL信息
    BoundSql boundSql = ms.getBoundSql(parameter);
    //调用simpleexecutor.doQueryCursor方法
    return doQueryCursor(ms, parameter, rowBounds, boundSql);
  }

//调用simpleexecutor.doQueryCursor方法
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
    return handler.<E>queryCursor(stmt);
  }

public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
    PreparedStatement ps = (PreparedStatement) statement;
    ps.execute();
    return resultSetHandler.<E> handleCursorResultSets(ps);
  }

//这里跟前面的解析基本一致  主要是把ResultMap 转换成DefaultCursor
public <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling cursor results").object(mappedStatement.getId());

    ResultSetWrapper rsw = getFirstResultSet(stmt);

    List<ResultMap> resultMaps = mappedStatement.getResultMaps();

    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    if (resultMapCount != 1) {
      throw new ExecutorException("Cursor results cannot be mapped to multiple resultMaps");
    }

    ResultMap resultMap = resultMaps.get(0);
    return new DefaultCursor<E>(this, resultMap, rsw, rowBounds);
  }

//把ResultMap 转换成DefaultCursor
public DefaultCursor(DefaultResultSetHandler resultSetHandler, ResultMap resultMap, ResultSetWrapper rsw, RowBounds rowBounds) {
        this.resultSetHandler = resultSetHandler;
        this.resultMap = resultMap;
        this.rsw = rsw;
        this.rowBounds = rowBounds;
    }

5.返回单一对象:这里直接调用的sqlsession.selectOne方法:主要是调用selectList方法

public <T> T selectOne(String statement, Object parameter) {
    // 调用selectList方法
    List<T> list = this.<T>selectList(statement, parameter);
    //这里判断  必须只能返回一条数据 如果多于1条  抛出异常
    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 {
      return null;
    }
  }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值