聊聊Mybatis的结果映射

举报
周杰伦本人 发表于 2022/08/30 13:43:44 2022/08/30
【摘要】 聊聊Mybatis的结果映射当Mybatis执行完sql获得ResultSet结果集的时候会交给ResultSetHandler进行处理,完成ResultSet到java对象的映射 结果集处理接口ResultSetHandler是一个接口,重点看一下public interface ResultSetHandler { <E> List<E> handleResultSets(State...

聊聊Mybatis的结果映射

当Mybatis执行完sql获得ResultSet结果集的时候会交给ResultSetHandler进行处理,完成ResultSet到java对象的映射

结果集处理接口

ResultSetHandler是一个接口,重点看一下

public interface ResultSetHandler {

  <E> List<E> handleResultSets(Statement stmt) throws SQLException;

  <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;

  void handleOutputParameters(CallableStatement cs) throws SQLException;

}

默认实现类是DefaultResultSetHandler,我们从它的handleResultSets()看起,这个方法一看名字就知道是处理ResultSet结果集的:

默认结果集处理类DefaultResultSetHandler

handleResultSets()方法

DefaultResultSetHandler的handleResultSets()方法:

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

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

    int resultSetCount = 0;
    ResultSetWrapper rsw = getFirstResultSet(stmt);

    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size();
    validateResultMapsCount(rsw, resultMapCount);
    while (rsw != null && resultMapCount > resultSetCount) {
      ResultMap resultMap = resultMaps.get(resultSetCount);
      handleResultSet(rsw, resultMap, multipleResults, null);
      rsw = getNextResultSet(stmt);
      cleanUpAfterHandlingResultSet();
      resultSetCount++;
    }

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

    return collapseSingleResultList(multipleResults);
  }
  1. 获取第一个ResultSet对象,并包装成ResultSetWrapper对象,ResultSetWrapper中保存了ResultSet的对象信息,包括列名,类名、jdbc类型和类型处理类TypeHandler
  2. 获取ResultMap集合,校验,然后遍历
  3. 调用handleResultSet()方法处理ResultSet,
  4. 处理嵌套映射
  5. 返回映射

重点看一下handleResultSet()方法:

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 {
        if (resultHandler == null) {
          DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
          handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
          multipleResults.add(defaultResultHandler.getResultList());
        } else {
          handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
        }
      }
    } finally {
      // issue #228 (close resultsets)
      closeResultSet(rsw.getResultSet());
    }
  }
  1. 如果是嵌套映射,调用handleRowValues()方法
  2. 如果是普通映射,也调用handleRowValues()方法
  3. 最后关闭ResultSet

重点看一下handleRowValues()方法:

public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
    if (resultMap.hasNestedResultMaps()) {
      ensureNoRowBounds();
      checkResultHandler();
      handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    } else {
      handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    }
  }

如果包含嵌套映射,就调用handleRowValuesForNestedResultMap()方法,否则调用handleRowValuesForSimpleResultMap()进行简单映射

先分析一下handleRowValuesForSimpleResultMap()方法:

handleRowValuesForSimpleResultMap()方法

private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
            throws SQLException {
        DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
        ResultSet resultSet = rsw.getResultSet();
        skipRows(resultSet, rowBounds);
        while (shouldProcessMoreRows(resultContext, rowBounds)
                && !resultSet.isClosed() && resultSet.next()) {
            ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
            Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
            storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
        }
    }
    
     private boolean shouldProcessMoreRows(ResultContext<?> context, RowBounds rowBounds) {
        return !context.isStopped() && context.getResultCount() < rowBounds.getLimit();
    }
  1. 跳过多余记录,定位到需要映射的记录
  2. shouldProcessMoreRows()方法是用来检测是否需要处理行数据,这个方法主要是判断行数是否达到了RowBounds的limit属性的限制,这个是分页的操作
  3. resolveDiscriminatedResultMap()方法用来获取需要映射的ResultMap规则
  4. 调用getRowValue()方法使用ResultMap规则对ResultSetWrapper中的ResultSet进行结果映射,返回对应的Java对象,首先根据ResultMap的type属性创建对应的结果对象,创建结果对象的时候如果只有一列,直接通过typeHandler得到结果对象,如果有<constructor>标签,就通过反射使用构造方法来创建结果对象,没有构造函数的标签就使用无参构造函数生成结果对象,如果没有默认构造函数通过自动映射来茶总合适的构造方法来进行生成结果对象,本质都是通过objectFactory.create()方法来生成的,然后再根据映射规则对属性进行填充,映射有分自动映射和正常映射,这里我 就不详细说了
  5. 记录结果到ResultHandler中

总结

这篇文章讲了一下Mybatis的结果映射,从DefaultResultSetHandler的handleResultSets()方法开始,一步一步深入,分析了怎么创建结果对象,怎么实现结果对象的属性映射,代码比较多,还是有点复杂难以理解的,还需要细细思考多读读源码

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。