mybatis源码分析(七):查询结果转换ResultSetHandler

本文详述了Mybatis的DefaultResultSetHandler如何处理查询结果,包括ResultWrapper的使用、处理单个结果集的逻辑、简单映射和嵌套映射的细节,尤其关注了通过discriminator确定ResultMap、自动映射及如何处理嵌套查询映射的过程。

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

ResultWrapper

在介绍ResultSetHandler之前,首先介绍下在ResultSetHandler中经常使用的ResultWrapper
ResultWrapper的主要作用就是对jdbc返回的查询结果ResultSet进行包裹,并且提供一些辅助能力,可以方便地从ResultSet中访问特定的列

重要属性

// jdbc返回的结果集
private final ResultSet resultSet;
// 类型转换器注册中心
private final TypeHandlerRegistry typeHandlerRegistry;
// 查询结果中每列的名称
private final List<String> columnNames = new ArrayList<>();
// 查询结果中每列的java类型
private final List<String> classNames = new ArrayList<>();
// 查询结果中每列的jdbc类型
private final List<JdbcType> jdbcTypes = new ArrayList<>();
// 每列对应的typeHandler
private final Map<String, Map<Class<?>, TypeHandler<?>>> typeHandlerMap = new HashMap<>();
// 记录了总的映射列名,key是ResultMap的id,value是该ResultMap的列名集合
private final Map<String, List<String>> mappedColumnNamesMap = new HashMap<>();
// 和上面的属性相反,记录了未映射的列名,key是ResultMap的id,value是该ResultMap未映射的列名集合
private final Map<String, List<String>> unMappedColumnNamesMap = new HashMap<>();

重要方法

构造方法
public ResultSetWrapper(ResultSet rs, Configuration configuration) throws SQLException {
   
   
  super();
  this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
  this.resultSet = rs;
  final ResultSetMetaData metaData = rs.getMetaData();
  final int columnCount = metaData.getColumnCount();
  for (int i = 1; i <= columnCount; i++) {
   
   
    // columnLable代表原始的列名,columnName代表别名
    columnNames.add(configuration.isUseColumnLabel() ? metaData.getColumnLabel(i) : metaData.getColumnName(i));
    jdbcTypes.add(JdbcType.forCode(metaData.getColumnType(i)));
    classNames.add(metaData.getColumnClassName(i));
  }
}

ResultSetHandler

在上一篇介绍Mybatis执行流程的时候,我们了解到,当从数据库获得查询数据之后,会使用ResultSetHandler对查询结果进行处理,下面看下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

mybatis只提供了一个实现类DefaultResultSetHandler

创建时机

它会在初始化StatementHandler的时候进行创建

protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
   
   
  this.configuration = mappedStatement.getConfiguration();
  this.executor = executor;
  this.mappedStatement = mappedStatement;
  this.rowBounds = rowBounds;

  this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
  this.objectFactory = configuration.getObjectFactory();

  if (boundSql == null) {
   
    // issue #435, get the key before calculating the statement
    generateKeys(parameterObject);
    boundSql = mappedStatement.getBoundSql(parameterObject);
  }

  this.boundSql = boundSql;

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

重要属性

private final Executor executor;
private final Configuration configuration;
private final MappedStatement mappedStatement;
private final RowBounds rowBounds;
private final ParameterHandler parameterHandler;
private final ResultHandler<?> resultHandler;
private final BoundSql boundSql;
private final TypeHandlerRegistry typeHandlerRegistry;
private final ObjectFactory objectFactory;
private final ReflectorFactory reflectorFactory;

重要方法

handleResultSets

因为Mybatis也支持使用存储过程来进行查询,存储过程查询的结果可能包含多个结果集,因此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;
  // 获取第一个查询结果集
  ResultSetWrapper rsw = getFirstResultSet(stmt);

  // 获得ResultMap,即查询结果的转换关系
  List<ResultMap> resultMaps = mappedStatement.getResultMaps();
  int resultMapCount = resultMaps.size();
  validateResultMapsCount(rsw, resultMapCount);
  // 遍历多个查询结果集,一般只有一个,除非调用的是存储过程
  while (rsw != null && resultMapCount > resultSetCount) {
   
   
    // 获取当前结果集对应的ResultMap
    ResultMap resultMap = resultMaps.get(resultSetCount);
    // 转换结果集,并且添加到multipleResults中
    handleResultSet(rsw, resultMap, multipleResults, null);
    // 获取下一个查询结果集
    rsw = getNextResultSet(stmt);
    // 清空嵌套resultMap
    cleanUpAfterHandlingResultSet();
    resultSetCount++;
  }

  // resultSets是针对多结果集的情况下,给每个结果集一个名称,多个名称之间使用,分割
  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);
}

多结果集只会在调用存储的时候出现,所以这里只介绍下单个结果集的情况

结果集处理

结果集的处理逻辑主要放在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
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值