MyBatis源码简读——2.3 4 ParameterHandler和ResultSetHandler

上一篇讲到PreparedStatementHandler的时候中间对参数处理的时候使用的是ParameterHandler;而在其查询最后也会使用一个resultSetHandler。本篇主要看看这两个类的逻辑

ParameterHandler

对SQL执行方法参数处理的顶级接口

/**
 * A parameter handler sets the parameters of the {@code PreparedStatement}.
 * 参数处理器接口
 * @author Clinton Begin
 */
public interface ParameterHandler {

  // 获得参数对象
  Object getParameterObject();

  // 设置PreparedStatement的占位符
  void setParameters(PreparedStatement ps)
      throws SQLException;
}

实现类

DefaultParameterHandler是ParameterHandler唯一的实现类

DefaultParameterHandler

实现 ParameterHandler 接口,默认 ParameterHandler 实现类

setParameters

是其主要处理参数的方法
但是仔细回想着,其实这一部分在SQL初始化的时候最后已经讲过了。
ParameterHandler进行参数替换

ResultSetHandler

对SQL执行结果的处理的顶级接口

/**
 * java.sql.ResultSet 处理器接口
 * @author Clinton Begin
 */
public interface ResultSetHandler {
    // 处理 {@link java.sql.ResultSet} 成映射的对应的结果
  <E> List<E> handleResultSets(Statement stmt) throws SQLException;

  // 处理 {@link java.sql.ResultSet} 成 Cursor 对象
  <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;

  // 暂时忽略,和存储过程相关
  void handleOutputParameters(CallableStatement cs) throws SQLException;
}

实现类

DefaultResultSetHandler是ResultSetHandler唯一的实现类

DefaultResultSetHandler

实现 DefaultResultSetHandler接口,默认 DefaultResultSetHandler实现类

handleResultSets
  //
  // HANDLE RESULT SETS
  // 处理 java.sql.ResultSet 结果集,转换成映射的对应结果
  // 仅仅支持处理 Statement 和 PreparedStatement 返回的结果集,
  // 也支持存储过程的 CallableStatement 返回的结果集。
  // 而 CallableStatement 是支持返回多结果集的,这个大家要注意
  @Override
  public List<Object> handleResultSets(Statement stmt) throws SQLException {
    ErrorContext.instance().activity("handling results").object(mappedStatement.getId());

    // 多 ResultSet 的结果集合,每个 ResultSet 对应一个 Object 对象。
    // 而实际上,每个 Object 是 List<Object> 对象。
    // 在不考虑存储过程的多 ResultSet 的情况,普通的查询,实际就一个 ResultSet ,
    // 也就是说,multipleResults 最多就一个元素。
    final List<Object> multipleResults = new ArrayList<>();

    int resultSetCount = 0;
    // 获取第一个返回结果对象,并封装成结果结果对象包装类
    ResultSetWrapper rsw = getFirstResultSet(stmt);

    // 从mappedStatement中获得结果的map
    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    // 取得结果Map的总数,然后进行验证
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    while (rsw != null && resultMapCount > resultSetCount) {
      // 获得resultMap
      ResultMap resultMap = resultMaps.get(resultSetCount);
      // 进行复制操作
      handleResultSet(rsw, resultMap, multipleResults, null);
      // 获得下一个封装对象
      rsw = getNextResultSet(stmt);
      // 清理上下文
      cleanUpAfterHandlingResultSet();
      // 索引增加
      resultSetCount++;
    }

    // getResultSets 存储过程使用
    String[] resultSets = mappedStatement.getResultSets();
    if (resultSets != null) {
      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);
        }
        rsw = getNextResultSet(stmt);
        cleanUpAfterHandlingResultSet();
        resultSetCount++;
      }
    }

    // 如果是 multipleResults 单元素,则取首元素返回
    return collapseSingleResultList(multipleResults);
  }

在不考虑存储过程的多 ResultSet 的情况,一般的查询mappedStatement.getResultMaps()只会拿到一个结果集映射,而不是出现多种映射
整个逻辑可以被拆分成这样:
首先从Statement获得其结果集ResultSet,然后通过ResultSetWrapper对其进行包装。
然后从mappedStatement中获得其结果映射集合。
依次从映射集合中获取单独项,进行结果复制复制到multipleResults中。
如果有多个结果,但是只需要返回一个,则取第一个返回。

handleResultSet
  private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
    try {
      if (parentMapping != null) {
        handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
      } else {
        //  如果没有自定义的 resultHandler ,则创建默认的 DefaultResultHandler 对象
        if (resultHandler == null) {
          // 创建一个默认的结果处理器
          DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
          // 处理 ResultSet 返回的每一行 Row
          handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
          // 添加处理结果
          multipleResults.add(defaultResultHandler.getResultList());
        } else {
          // 理 ResultSet 返回的每一行 Row
          handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
        }
      }
    } finally {
      // issue #228 (close resultsets)
      // 关闭结果集对象
      closeResultSet(rsw.getResultSet());
    }
  }


public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
    // 假如嵌套映射
    if (resultMap.hasNestedResultMaps()) {
      // 校验不要使用 RowBounds
      ensureNoRowBounds();
      // 校验不要使用自定义的 resultHandler
      checkResultHandler();
      // 处理嵌套映射的结果
      handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    } else {
      // 处理简单映射关系
      handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    }
  }

进行参数处理的方法

最终都是使用handleRowValues进行参数替换的。我们看下简单的映射关系。

handleRowValuesForSimpleResultMap
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
      throws SQLException {
    // 创建 DefaultResultContext 对象
    DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
    // 获得 ResultSet 对象,并跳到 rowBounds 指定的开始位置
    ResultSet resultSet = rsw.getResultSet();
    skipRows(resultSet, rowBounds);
    // 循环
    while (shouldProcessMoreRows(resultContext, rowBounds) // 是否还需要处理
            && !resultSet.isClosed()  // 是否已经关闭
            && resultSet.next()) { // 是否结束
      // 根据该行记录以及 ResultMap.discriminator ,决定映射使用的 ResultMap 对象
      ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
      // 根据最终确定的 ResultMap 对 ResultSet 中的该行记录进行映射,得到映射后的结果对象
      Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
      // 将映射创建的结果对象添加到 ResultHandler.resultList 中保存
      storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
    }
  }

storeObject

将值保存到ResultHandler.resultList中

private void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException {
    if (parentMapping != null) {
      linkToParents(rs, parentMapping, rowValue);
    } else {
      callResultHandler(resultHandler, resultContext, rowValue);
    }
  }

  // 调用 ResultHandler ,进行结果的处理
  @SuppressWarnings("unchecked" /* because ResultHandler<?> is always ResultHandler<Object>*/)
  private void callResultHandler(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue) {
    // 设置结果对象到 resultContext 中
    resultContext.nextResultObject(rowValue);
    // 使用 ResultHandler 处理结果。
    // 如果使用 DefaultResultHandler 实现类的情况,会将映射创建的结果对象添加到 ResultHandler.resultList 中保存
    ((ResultHandler<Object>) resultHandler).handleResult(resultContext);
  }

// org/apache/ibatis/executor/result/DefaultResultHandler.java
@Override
  public void handleResult(ResultContext<?> context) {
      // 将当前结果,添加到结果数组中 
    list.add(context.getResultObject());
  }

总结:

  • ParameterHandler的方法其实在初始化SQL的过程中我们已经看过的,就不多介绍了。
  • ResultSetHandler的整个逻辑的核心在handleResultSet上面(不考虑存储过程)其他的不过是数据获取和效验。
  • 继续深扒整个方法嵌套是handleRowValues ->handleRowValuesForSimpleResultMap是进行最后数据复制的地方。
  • 通过resolveDiscriminatedResultMap获得具体的映射对象,然后在getRowValue获得对应的值,然后在storeObject将值绑定到对应的DefaultResultHandler.list中
  • 里面很多细节其实并没有说清,强烈建议边调试边查看。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大·风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值