第七章-Mybatis源码解析-ResultSetHandler结果集处理

第六章已经讲了语句的处理,本章开始讲下针对结果集的处理,从6.36.4中得到,结果集的入口方法是同一个,都是 resultSetHandler.handleResultSets ,ResultSetHandler 是一个接口类,在Mybatis中,只有一个实现类 DefaultResultSetHandler,那直接进入这个类的 handleResultSets 方法就行。

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;
    // 内部通过jdbc方式,拿到ResultSet,并包装成Mybatis中的ResultSetWrapper
    ResultSetWrapper rsw = getFirstResultSet(stmt);
    // 语句的ResultMap集合,多个 ResultMap 配置时通过","分隔
    List<ResultMap> resultMaps = mappedStatement.getResultMaps();
    int resultMapCount = resultMaps.size(); // ResultMap 集数量
    validateResultMapsCount(rsw, resultMapCount); // 验证 必须是 >= 1的
    while (rsw != null && resultMapCount > resultSetCount) { // 遍历
        // 取出 resultMap
        ResultMap resultMap = resultMaps.get(resultSetCount);
        // 真正处理结果集的方法,看章节`7.1`
        handleResultSet(rsw, resultMap, multipleResults, null);
        // 这个方法只有在支持多结果时,才会有值,否则返回 null
        rsw = getNextResultSet(stmt);
        // 清空处理过程中产生的未处理完的,其实就是有异常,没法处理的
        cleanUpAfterHandlingResultSet();
        // 自增1
        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);
}

// 这个方法里面都是jdbc的操作,建议先了解下jdbc操作数据库的逻辑
private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
    ResultSet rs = stmt.getResultSet(); // 获取结果集
    while (rs == null) { 
        // move forward to get the first resultset in case the driver
        // doesn't return the resultset as the first result (HSQLDB 2.1)
        if (stmt.getMoreResults()) {
            rs = stmt.getResultSet();
        } else {
            if (stmt.getUpdateCount() == -1) {
                // no more results. Must be no resultset
                break;
            }
        }
    }
    // rs 不为 null 的时候,直接用 ResultSetWrapper 包装类封装一下
    return rs != null ? new ResultSetWrapper(rs, configuration) : null;
}

ResultSetWrapper构造函数

public ResultSetWrapper(ResultSet rs, Configuration configuration) throws SQLException {
    super();
    // 存放类型处理器容器
    this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
    this.resultSet = rs;
    // 结果集的元数据(一般有列的数量、字段信息、类型等),想了解更多,可以去看jdbc相关API
    final ResultSetMetaData metaData = rs.getMetaData();
    // 查出的列数量
    final int columnCount = metaData.getColumnCount();
    // 遍历列
    for (int i = 1; i <= columnCount; i++) {
        // 列名/别名
        columnNames.add(configuration.isUseColumnLabel() ? metaData.getColumnLabel(i) : metaData.getColumnName(i));
        // 数据库类型 到 jdbc类型的转换
        jdbcTypes.add(JdbcType.forCode(metaData.getColumnType(i)));
        // 列类型的类名
        classNames.add(metaData.getColumnClassName(i));
    }
}

7.1 handleResultSet

// 这个方法有多个if...else分支,但是不管怎么样,最终都会走到 handleRowValues 方法,继续探路
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());
    }
}

handleRowValues

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 {
        handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
    }
}

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); // 跳过不要的行
    // 跳过 offset 的行数后,如果后面还有数据,就取出来自己要的行数
    while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) { // 移动游标,一行行找
        /* discriminator – 使用结果值来决定使用哪个 resultMap
			case – 基于某些值的结果映射
				嵌套结果映射 – case 也是一个结果映射,因此具有相同的结构和元素;或者引用其它的结果映射
		*/
        // discriminator 的处理,这里不做扩展
        ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
        // 取出每一行的值,并生成返回类型对象
        Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
        // 将结果存储到 resultHandler 的定义的容器中,默认 DefaultResultHandler 是存放到 List 中,这也就是为什么在SqlSession底层,哪怕查询一条数据,也是走的selectList方法,ResultHandler 是一个接口类,可以自行扩展
        storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
    }
}

skipRows

这就类似分页操作,并且是基于内存的分页,与之对应的是基于物理的分页,也就是在sql中增加limit来实现

private void skipRows(ResultSet rs, RowBounds rowBounds) throws SQLException {
    if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) {
        if (rowBounds.getOffset() != RowBounds.NO_ROW_OFFSET) {
            rs.absolute(rowBounds.getOffset()); // 跳转到设置的起始偏移行
        }
    } else {
        for (int i = 0; i < rowBounds.getOffset(); i++) {
            if (!rs.next()) {
                break;
            }
        }
    }
}		

截止目前,关于xml方式的流程已经完结,这一章写的有点草率,主要原因是本人感冒了,有点写不下去了,但是主流流程还是没有缺失的,只是一些细节方法没有走进去讲,将就着看吧,有兴趣的读者可以自行扩展,或者评论区讨论下哪里需要深入讲解的,如有需要后续再补充。

  • 8
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

多栖码农

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

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

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

打赏作者

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

抵扣说明:

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

余额充值