Mybatis的结果集处理

这篇博客详细解析了Mybatis如何处理SQL查询后得到的结果集,包括使用、resultType属性进行映射,以及DefaultResultSetHandler如何处理多结果集,特别是对于存储过程的处理。文中还举例介绍了鉴别器(discriminator)的用法,如何根据特定列的值改变封装行为,以及在复杂对象映射中的应用。
摘要由CSDN通过智能技术生成

此时我们已经可以把整段的SQL语句取出,但还并没有在数据库中去执行,我们可以先来分析一下配置文件中SQL语句执行后的结果集是如何处理的。

Mybatis会将结果集按照映射配置文件中定义的映射规则,例如<resultMap>,resultType属性等,映射成相应的结果对象。

在StatementHandler接口执行完指定的select语句之后,会将查询得到的结果集交给ResultSetHandler完成映射处理。ResultSetHandler除了负责映射select语句查询得到的结果集,还会处理存储过程执行后的输出参数。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;

}

ResultSetHandler只有一个实现类DefaultResultSetHandler,其核心字段如下

private final Executor executor;  //执行器对象
private final Configuration configuration; //全局配置信息对象
private final MappedStatement mappedStatement; //记录了SQL语句节点所有信息的对象(<select><insert><update>节点等)
private final RowBounds rowBounds; //逻辑分页对象(不同于SQL语句中的limit物理分页)
private final ParameterHandler parameterHandler; //参数处理器
private final ResultHandler<?> resultHandler; //用户指定用于处理结果集的ResultHandler对象
private final BoundSql boundSql; //SqlSource执行后返回的包含完整SQL语句的对象
private final TypeHandlerRegistry typeHandlerRegistry; //类型处理器注册器
private final ObjectFactory objectFactory; //对象工厂
private final ReflectorFactory reflectorFactory; //反射工厂
private final Map<CacheKey, Object> nestedResultObjects = new HashMap<CacheKey, Object>(); //嵌套的resultMap对象映射
private Object previousRowValue; //上一次嵌套的resultMap对象
//PendingRelation是DefaultResultSetHandler的内部静态类,记录了当前结果对象对应的MetaObject对象以及parentMapping对象
//该对象就为CacheKey对象跟全部的PendingRelation对象的映射
private final Map<CacheKey, List<PendingRelation>> pendingRelations = new HashMap<CacheKey, List<PendingRelation>>();

通过select语句查询数据库得到的结果集由其handlerResultSets()方法进行处理。该方法不仅可以处理Statement,PreparedStatement产生的结果集,还可以处理CallableStatement调用存储过程产生的多结果集。例如如下存储过程,就会产生多个ResultSet对象。

CREATE PROCEDURE test_proc()

BEGIN

        select * from persion;

        select * from item;

END;

handleResultSets()方法如下

@Override
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;
  //获取第一个ResultSet对象,可能存在多个ResultSet
  ResultSetWrapper rsw = getFirstResultSet(stmt);
  //获取MappedStatement.resultMaps集合,映射文件中的<resultMap>节点会被解析成ResultMap对象,保存到MappedStatement.resultMaps集合中
  //如果SQL节点能够产生多个ResultSet,那么我们可以在SQL节点的resultMap属性中配置多个<resultMap>节点的id,它们之间通过","分隔,实现对多个
  //结果集的映射
  List<ResultMap> resultMaps = mappedStatement.getResultMaps();
  int resultMapCount = resultMaps.size();
  //如果结果集不为null,则resultMaps集合(List)不能为空,否则抛出异常
  validateResultMapsCount(rsw, resultMapCount);
  //遍历resultMaps集合
  while (rsw != null && resultMapCount > resultSetCount) {
    //取出resultMaps集合中的每一个ResultMap对象
    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);
}
private ResultSetWrapper getFirstResultSet(Statement stmt) throws SQLException {
  //Statement是JDBC数据操作的接口,由Connection连接数据库后创建,由各种不同的数据库驱动来创建实现类,由于我们项目最近使用的是mysql 8版本的数据库,它的实现类已经跟
  //以往的mysql版本大不相同,此处为获取结果集
  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)
    //检测是否还有待处理的ResultSet
    if (stmt.getMoreResults()) {
      rs = stmt.getResultSet();
    } else {
      if (stmt.getUpdateCount() == -1) { //没有待处理的ResultSet
        // no more results. Must be no resultset
        break;
      }
    }
  }
  //将结果集封装成ResultSetWrapper对象
  return rs != null ? new ResultSetWrapper(rs, configuration) : null;
}
private void validateResultMapsCount(ResultSetWrapper rsw, int resultMapCount) {
  if (rsw != null && resultMapCount < 1) {
    throw new ExecutorException("A query was run and no Result Maps were found for the Mapped Statement '" + mappedStatement.getId()
        + "'.  It's likely that neither a Result Type nor a Result Map was specified.");
  }
}
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值