【源码解析】MyBatis结果集映射和参数绑定
前言:📫 作者简介:小明java问道之路,专注于研究计算机底层,就职于金融公司后端高级工程师,擅长交易领域的高安全/可用/并发/性能的设计和架构📫
🏆 Java领域优质创作者、阿里云专家博主、华为云专家🏆
🔥 如果此文还不错的话,还请👍关注、点赞、收藏三连支持👍一下博主哦
本文导读
【文章较长,建议收藏】本文讲解MyBatis结果集映射源码解析,详细分析了 handleRowValuesForSimpleResultMap() 等方法实现映射的核心步骤
Mybatis系列文章:
:MyBatis整体架构
: MyBatis工作流程源码解析与MyBatis中的设计模式
:Mybatis动态sql源码解析及动态sql的执行原理
【总】MyBatis结果集映射是在MyBatis 解析 Mapper.xml 映射文件的过程中, <resultMap> 标签会被解析成 ResultMap 对象的,当 MyBatis 执行完一条 select 语句,拿到 ResultSet 结果集之后,会将其交给关联的 ResultSetHandler 进行后续的映射处理。ResultSetHandler 其中定义了三个方法,分别用来处理不同的查询返回值,MyBatis 中只提供了一个 ResultSetHandler 接口实现,即 DefaultResultSetHandler。
ResultSetHandler 接口以及 DefaultResultSetHandler 这个默认实现,其中单个结果集映射的入口在handleResultSets() 方法。handleRowValuesForSimpleResultMap() 方法实现简单映射的核心步骤,其中涉及预处理 ResultSet、查找并确定 ResultMap、创建并填充映射结果对象、自动映射、正常映射、存储映射结果对象这六大核心步骤。
一、结果集处理入口
DefaultResultSetHandler实现 handleResultSets() 方法,支持一个或多个ResultSet的处理。org.apache.ibatis.executor.resultset.DefaultResultSetHandler#handleResultSets。
遍历多结果集时使用到的 getFirstResultSet() 方法和 getNextResultSet() 方法,这两个方法底层都是检测是否存在后续的 ResultSet 对象,检测成功之后,会通过 getResultSet() 方法获取下一个 ResultSet 对象。这里获取到的 ResultSet 对象,会被包装成 ResultSetWrapper 对象返回。
ResultSetWrapper 主要用于封装 ResultSet 的一些元数据,其中记录了 ResultSet 中每列的名称、对应的 Java 类型、JdbcType 类型以及每列对应的 TypeHandler。ResultSetWrapper 可以将底层 ResultSet 的列与一个 ResultMap 映射的列进行交集,得到参与映射的列和未被映射的列,分别记录到 mappedColumnNamesMap 集合和 unMappedColumnNamesMap 集合中。这两个集合都是 Map<String, List<String>>
类型,其中最外层的 Key 是 ResultMap 的 id,Value 分别是参与映射的列名集合和未被映射的列名集合。
二、简单映射
handleResultSet() 方法,其中会根据第四个参数,也就是 parentMapping,判断当前要处理的 ResultSet 是嵌套映射,还是外层映射。都会依赖 handleRowValues() 方法完成结果集的处理(通过方法名也可以看出,handleRowValues() 方法是处理多行记录的,也就是一个结果集)。
至于 handleRowValues() 方法,其中会通过 handleRowValuesForNestedResultMap() 方法处理包含嵌套映射的 ResultMap,通过 handleRowValuesForSimpleResultMap() 方法处理不包含嵌套映射的简单 ResultMap,如下所示:
handleRowValuesForSimpleResultMap() 方法如何映射一个 ResultSet 的,该方法的核心步骤可总结为如下。
handleRowValuesForSimpleResultMap() 方法如何映射一个 ResultSet 总结下来:第一步1. ResultSet 的预处理();第二步2. 确定 ResultMap,通过 resolveDiscriminatedResultMap() 方法处理 <discriminator>
标签,确定此次映射操作最终使用的 ResultMap 对象;第三步3. 创建映射结果对象,经过 resolveDiscriminatedResultMap() 方法解析,我们最终确定了当前记录使用哪个 ResultMap 进行映射。接下来按照 ResultMap 规则进行各个列的映射,得到最终的 Java 对象,这部分逻辑是在下面要介绍的 getRowValue() 方法完成的,其核心步骤如下:
第四步4. 自动映射,创建完结果对象之后,下面就可以开始映射各个字段了。在简单映射流程中,会先通过 shouldApplyAutomaticMappings() 方法检测是否开启了自动映射,主要检测以下两个地方。当确定当前 ResultMap 需要进行自动映射的时候,会通过 applyAutomaticMappings() 方法进行自动映射。
private void storeObject(ResultHandler<?> resultHandler, DefaultResultContext<Object> resultContext, Object rowValue, ResultMapping parentMapping, ResultSet rs) throws SQLException {
if (parentMapping != null) {
// 嵌套查询或嵌套映射的场景,此时需要将结果对象保存到外层对象对应的属性中
linkToParents(rs, parentMapping, rowValue);
} else {
// 普通映射(没有嵌套映射)或是嵌套映射中的外层映射的场景,此时需要将结果对象保存到ResultHandler中
callResultHandler(resultHandler, resultContext, rowValue);
}
}
总结
- 点赞
- 收藏
- 关注作者
评论(0)