全部学科
NodeJS全栈
nodejs
Python全栈
python
小程序首页
📅 2026-05-20 8 分钟 ✍️ juanwangdev

ResultSetHandler 结果处理

ResultSetHandler 是 MyBatis 四大核心组件之一,负责将 JDBC ResultSet 自动映射为 Java 对象。它是 MyBatis ORM 能力的核心体现。

ResultSetHandler 体系

Java
ResultSetHandler(接口)
  └── DefaultResultSetHandler(唯一实现)
        ├── handleResultSets()    // 处理多个结果集
        ├── handleCursorResultSets()  // 处理游标结果
        └── handleOutputParameters()  // 处理存储过程输出参数

注意:与 ParameterHandler 一样,ResultSetHandler 也只有 DefaultResultSetHandler 一个实现。

核心接口

Java
public interface ResultSetHandler {
  
  // 处理结果集(返回 List)
  <E> List<E> handleResultSets(Statement stmt) throws SQLException;
  
  // 处理游标结果集(流式查询)
  <E> Cursor<E> handleCursorResultSets(Statement stmt) throws SQLException;
  
  // 处理存储过程的输出参数
  void handleOutputParameters(CallableStatement cs) throws SQLException;
}

handleResultSets 执行流程

Java
@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);
  
  // 获取 ResultMap 列表(一个 SQL 可能有多个 ResultMap)
  List<ResultMap> resultMaps = mappedStatement.getResultMaps();
  int resultMapCount = resultMaps.size();
  
  validateResultMapsCount(rsw, resultMapCount);
  
  // 遍历处理每个结果集
  while (rsw != null && resultSetCount < resultMapCount) {
    ResultMap resultMap = resultMaps.get(resultSetCount);
    
    // 处理当前结果集
    handleResultSet(rsw, resultMap, multipleResults, null);
    
    // 获取下一个结果集
    rsw = getNextResultSet(stmt);
    resultSetCount++;
  }
  
  // 处理嵌套结果集(<collection> 中的嵌套查询)
  return collapseSingleResultList(multipleResults);
}

ResultSetWrapper 包装

Java
public class ResultSetWrapper {
  
  private final ResultSet resultSet;
  private final TypeHandlerRegistry typeHandlerRegistry;
  
  // 列名列表
  private final List<String> columnNames = new ArrayList<>();
  
  // 列的 JDBC 类型
  private final List<JdbcType> jdbcTypes = new ArrayList<>();
  
  // 列名 -> TypeHandler 映射
  private final Map<String, TypeHandler<?>> typeHandlerMap = new HashMap<>();
  
  // 列名 -> 列索引映射
  private final Map<String, String> columnMap = new HashMap<>();
  
  public ResultSetWrapper(ResultSet rs, Configuration configuration) throws SQLException {
    super();
    this.typeHandlerRegistry = configuration.getTypeHandlerRegistry();
    this.resultSet = rs;
    
    // 读取元数据
    ResultSetMetaData metaData = rs.getMetaData();
    int columnCount = metaData.getColumnCount();
    
    for (int i = 1; i <= columnCount; i++) {
      String columnName = metaData.getColumnLabel(i);
      columnNames.add(columnName);
      jdbcTypes.add(JdbcType.forCode(metaData.getColumnType(i)));
    }
  }
}

列名获取:使用 getColumnLabel() 而非 getColumnName(),这样可以获取到 SQL 中的 AS 别名。

handleResultSet 处理流程

Java
private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
  try {
    if (parentMapping != null) {
      // 嵌套结果集处理
      handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, null);
    } else {
      // 普通结果集处理
      if (resultHandler == null) {
        // 创建默认 ResultHandler
        ResultHandler<Object> handler = new DefaultResultHandler<>(objectFactory);
        handleRowValues(rsw, resultMap, handler, rowBounds, null);
        multipleResults.add(handler.getResultList());
      } else {
        handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
      }
    }
  } finally {
    closeResultSet(rsw.getResultSet());
  }
}

handleRowValues 核心逻辑

XML
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
  // 判断是否有嵌套结果或延迟加载
  if (shouldProcessMoreRows(resultMap, rowBounds)) {
    // 复杂结果映射(有关联、集合、延迟加载)
    handleRowValuesForComplexResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
  } else {
    // 简单结果映射(基本字段映射)
    handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
  }
}

ResultMap 解析

定义示例

Java
<resultMap id="userResultMap" type="User">
  <id property="id" column="user_id"/>
  <result property="name" column="user_name"/>
  <result property="age" column="user_age"/>
  <association property="address" javaType="Address">
    <id property="id" column="addr_id"/>
    <result property="city" column="addr_city"/>
  </association>
  <collection property="orders" ofType="Order">
    <id property="id" column="order_id"/>
    <result property="amount" column="order_amount"/>
  </collection>
</resultMap>

ResultMap 内部结构

Java
public class ResultMap {
  
  private String id;                          // resultMap ID
  private Class<?> type;                      // 目标 Java 类型
  private List<ResultMapping> resultMappings; // 所有映射(id + result + association + collection)
  private List<ResultMapping> idResultMappings;  // 主键映射
  private List<ResultMapping> propertyResultMappings;  // 普通字段映射
  private List<ResultMapping> constructorResultMappings;  // 构造函数映射
  private List<ResultMapping> nestedResultMappings;  // 嵌套映射(association/collection)
  private Boolean autoMapping;                  // 是否自动映射
  private ResultMapping mappedByResultMap;     // 被哪个 ResultMap 映射
}

自动映射原理

自动映射类型

Java
public enum AutoMappingBehavior {
  NONE,       // 不自动映射
  PARTIAL,    // 仅自动映射没有嵌套的结果(默认)
  FULL        // 全部自动映射,包括嵌套
}

自动映射处理

Java
// 在 handleRowValuesForSimpleResultMap 中
private void applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
  
  // 获取未显式映射的列
  List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, columnPrefix);
  
  if (!autoMapping.isEmpty()) {
    // 遍历自动映射
    for (UnMappedColumnAutoMapping mapping : autoMapping) {
      // 从 ResultSet 获取值
      Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.columnName);
      
      // 设置到 Java 对象属性
      if (value != null || configuration.isCallSettersOnNulls()) {
        metaObject.setValue(mapping.property, value);
      }
    }
  }
}

private List<UnMappedColumnAutoMapping> createAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
  
  // 获取所有列名
  List<String> unmappedColumnNames = rsw.getUnmappedColumnNames(resultMap, columnPrefix);
  
  List<UnMappedColumnAutoMapping> autoMapping = new ArrayList<>();
  
  for (String columnName : unmappedColumnNames) {
    // 列名转换为属性名(下划线转驼峰)
    String propertyName = columnToProperty(columnName);
    
    if (metaObject.hasSetter(propertyName)) {
      // 获取 TypeHandler
      Class<?> propertyType = metaObject.getSetterType(propertyName);
      TypeHandler<?> typeHandler = typeHandlerRegistry.getTypeHandler(propertyType, rsw.getJdbcType(columnName));
      
      autoMapping.add(new UnMappedColumnAutoMapping(columnName, propertyName, typeHandler, propertyType, configuration.isMapUnderscoreToCamelCase()));
    }
  }
  
  return autoMapping;
}

自动映射条件:列名在 ResultMap 中未显式映射 + Java 对象有对应 setter 方法 + autoMappingBehavior != NONE。

显式映射处理

Java
private void applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
  
  // 获取显式定义的映射
  List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
  
  for (ResultMapping resultMapping : resultMap.getPropertyResultMappings()) {
    String column = applyColumnPrefix(resultMapping.getColumn(), columnPrefix);
    
    if (mappedColumnNames.contains(column)) {
      // 获取 TypeHandler
      TypeHandler<?> typeHandler = resultMapping.getTypeHandler();
      
      // 从 ResultSet 获取值
      Object value = typeHandler.getResult(rsw.getResultSet(), column);
      
      // 设置到 Java 对象
      metaObject.setValue(resultMapping.getProperty(), value);
    }
  }
}

嵌套结果集处理

association 嵌套

Java
private Object getNestedQueryMappingValue(ResultSet rs, MetaObject metaResultObject, ResultMapping resultMapping, String columnPrefix) throws SQLException {
  
  String nestedQueryId = resultMapping.getNestedQueryId();
  MappedStatement nestedQuery = configuration.getMappedStatement(nestedQueryId);
  
  // 构建嵌套查询的参数
  Object parameter = getNestedQueryParameterValue(rs, resultMapping, columnPrefix);
  
  // 检查嵌套查询是否已经执行过(防止 N+1 问题)
  CacheKey cacheKey = createCacheKeyForNestedQuery(nestedQuery, parameter);
  
  Object nestedResult = localCache.getObject(cacheKey);
  if (nestedResult == null) {
    // 执行嵌套查询
    nestedResult = executor.query(nestedQuery, parameter, RowBounds.DEFAULT, null);
    localCache.putObject(cacheKey, nestedResult);
  }
  
  return nestedResult;
}

collection 嵌套

XML
// 处理集合嵌套(一对多)
private void handleCollection(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix, Object rowValue) throws SQLException {
  
  for (ResultMapping resultMapping : resultMap.getCollectionResultMappings()) {
    String property = resultMapping.getProperty();
    
    if (resultMapping.getNestedQueryId() != null) {
      // 嵌套查询方式
      Collection<Object> collection = (Collection<Object>) getNestedQueryMappingValue(rsw.getResultSet(), metaObject, resultMapping, columnPrefix);
      metaObject.setValue(property, collection);
    } else if (resultMapping.getNestedResultMapId() != null) {
      // 嵌套结果集方式
      ResultMap nestedResultMap = configuration.getResultMap(resultMapping.getNestedResultMapId());
      
      // 创建新对象用于嵌套
      Object nestedResult = createResultObject(nestedResultMap);
      
      // 递归处理嵌套结果
      handleResultSet(rsw, nestedResultMap, null, null, resultMapping);
      
      metaObject.setValue(property, nestedResult);
    }
  }
}

嵌套方式对比

Java
<!-- 方式1:嵌套查询(N+1 问题) -->
<association property="address" column="addr_id" select="findAddressById"/>

<!-- 方式2:嵌套结果集(JOIN 查询) -->
<association property="address" javaType="Address">
  <id property="id" column="addr_id"/>
  <result property="city" column="addr_city"/>
</association>
特性嵌套查询(select)嵌套结果集(resultMap)
SQL执行次数N+1次1次(JOIN)
性能差(N次额外查询)好(单次JOIN)
延迟加载支持不支持
适用场景按需加载、大数据量固定关联数据

延迟加载机制

延迟加载触发条件

Java
public boolean shouldApplyAutomaticMappings(ResultMap resultMap, boolean isNested) {
  if (resultMap.getAutoMapping() != null) {
    return resultMap.getAutoMapping();
  }
  
  // 根据配置判断
  if (isNested) {
    return configuration.getAutoMappingBehavior() == AutoMappingBehavior.FULL;
  }
  return configuration.getAutoMappingBehavior() != AutoMappingBehavior.NONE;
}

延迟加载代理

text
// 创建延迟加载代理对象
private Object createLazyLoadingProxy(Object target, String property, ResultLoaderMap lazyLoader) {
  
  // 使用 Javassist 或 CGLIB 创建代理
  return Proxy.newProxyInstance(
    target.getClass().getClassLoader(),
    target.getClass().getInterfaces(),
    (proxy, method, args) -> {
      if ("equals".equals(method.getName())) {
        return target == args[0];
      }
      
      if ("hashCode".equals(method.getName())) {
        return System.identityHashCode(target);
      }
      
      // 触发实际加载
      lazyLoader.load(property);
      return method.invoke(target, args);
    });
}

结果对象创建流程

text
handleRowValues()
    |
    +-- createResultObject()
    |       |
    |       +-- 有 @MapKey 注解 → 返回 Map
    |       +-- 有 ResultType 且是接口 → 返回 Map
    |       +-- 使用 ObjectFactory 创建实例
    |
    +-- applyAutomaticMappings()   ← 自动映射
    |
    +-- applyPropertyMappings()    ← 显式映射
    |
    +-- handleNestedResultMappings() ← 嵌套映射
    |
    +-- 返回 Java 对象

调用时序图

text
StatementHandler.query()
    |
    +-- ps.execute()
    |
    +-- resultSetHandler.handleResultSets(ps)
    |       |
    |       +-- getFirstResultSet() → ResultSetWrapper
    |       |
    |       +-- handleResultSet()
    |       |       |
    |       |       +-- handleRowValues()
    |       |       |       |
    |       |       |       +-- createResultObject()
    |       |       |       +-- applyAutomaticMappings()
    |       |       |       +-- applyPropertyMappings()
    |       |       |       +-- handleNestedResultMappings()
    |       |       |
    |       |       +-- 添加到结果列表
    |       |
    |       +-- getNextResultSet() → 循环处理
    |
    +-- 返回 List<Java对象>

要点总结

  • 唯一实现:DefaultResultSetHandler 是 ResultSetHandler 的唯一实现,所有结果处理逻辑集中在此
  • ResultWrapper 包装:将 ResultSet 和元数据包装为 ResultSetWrapper,便于后续映射处理
  • 自动映射条件:列名未显式映射 + 有 setter 方法 + autoMappingBehavior != NONE,默认 PARTIAL 模式
  • 自动映射规则:列名转属性名时支持下划线转驼峰,根据 TypeHandlerRegistry 自动选择类型处理器
  • 嵌套查询 vs 嵌套结果集:嵌套查询产生 N+1 问题但支持延迟加载,嵌套结果集通过 JOIN 一次查询性能更好
  • 延迟加载原理:通过 JDK 动态代理拦截方法调用,在首次访问属性时才执行实际查询
  • 多结果集处理:handleResultSets 支持处理多个 ResultSet,通常用于存储过程返回多个结果集的场景

📝 发现内容有误?点击此处直接编辑

← 上一篇 ParameterHandler 参数处理
下一篇 → SqlSession 生命周期
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库