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,通常用于存储过程返回多个结果集的场景
📝 发现内容有误?点击此处直接编辑