MyBatis 源码分析 - 映射文件解析过程 (7)

第二种配置方式是采取 resultMap 嵌套的方式进行配置,如下:

<resultMap type="Article"> <id property="id" column="id"/> <result property="title" column="article_title"/> <!-- resultMap 嵌套 --> <association property="article_author" javaType="Author"> <id property="id" column="author_id"/> <result property="name" column="author_name"/> </association> </resultMap>

如上配置,<association> 的子节点是一些结果映射配置,这些结果配置最终也会被解析成 ResultMap。我们可以看看解析过程是怎样的,如下:

private String processNestedResultMappings(XNode context, List<ResultMapping> resultMappings) throws Exception { // 判断节点名称 if ("association".equals(context.getName()) || "collection".equals(context.getName()) || "case".equals(context.getName())) { if (context.getStringAttribute("select") == null) { // resultMapElement 是解析 ResultMap 入口方法 ResultMap resultMap = resultMapElement(context, resultMappings); // 返回 resultMap id return resultMap.getId(); } } return null; }

如上,<association> 的子节点由 resultMapElement 方法解析成 ResultMap,并在最后返回 resultMap.id。对于 <resultMap> 节点,id 的值配置在该节点的 id 属性中。但 <association> 节点无法配置 id 属性,那么该 id 如何产生的呢?答案在 XNode 类的 getValueBasedIdentifier 方法中,这个方法具体逻辑我就不分析了。下面直接看一下以上配置中的 <association> 节点解析成 ResultMap 后的 id 值,如下:

id = mapper_resultMap[articleResult]_association[article_author]

关于嵌套 resultMap 的解析逻辑就先分析到这,下面分析 ResultMapping 的构建过程。

public ResultMapping buildResultMapping(Class<?> resultType, String property, String column, Class<?> javaType,JdbcType jdbcType, String nestedSelect, String nestedResultMap, String notNullColumn, String columnPrefix,Class<? extends TypeHandler<?>> typeHandler, List<ResultFlag> flags, String resultSet, String foreignColumn, boolean lazy) { /* * 若 javaType 为空,这里根据 property 的属性进行解析。关于下面方法中的参数, * 这里说明一下: * - resultType:即 <resultMap type="xxx"/> 中的 type 属性 * - property:即 <result property="xxx"/> 中的 property 属性 */ Class<?> javaTypeClass = resolveResultJavaType(resultType, property, javaType); // 解析 TypeHandler TypeHandler<?> typeHandlerInstance = resolveTypeHandler(javaTypeClass, typeHandler); /* * 解析 column = {property1=column1, property2=column2} 的情况, * 这里会将 column 拆分成多个 ResultMapping */ List<ResultMapping> composites = parseCompositeColumnName(column); // 通过建造模式构建 ResultMapping return new ResultMapping.Builder(configuration, property, column, javaTypeClass) .jdbcType(jdbcType) .nestedQueryId(applyCurrentNamespace(nestedSelect, true)) .nestedResultMapId(applyCurrentNamespace(nestedResultMap, true)) .resultSet(resultSet) .typeHandler(typeHandlerInstance) .flags(flags == null ? new ArrayList<ResultFlag>() : flags) .composites(composites) .notNullColumns(parseMultipleColumnNames(notNullColumn)) .columnPrefix(columnPrefix) .foreignColumn(foreignColumn) .lazy(lazy) .build(); } // -☆- ResultMapping.Builder public ResultMapping build() { // 将 flags 和 composites 两个集合变为不可修改集合 resultMapping.flags = Collections.unmodifiableList(resultMapping.flags); resultMapping.composites = Collections.unmodifiableList(resultMapping.composites); // 从 TypeHandlerRegistry 中获取相应 TypeHandler resolveTypeHandler(); validate(); return resultMapping; }

ResultMapping 的构建过程不是很复杂,首先是解析 javaType 类型,并创建 typeHandler 实例。然后处理复合 column。最后通过建造器构建 ResultMapping 实例。关于上面方法中出现的一些方法调用,这里接不跟下去分析了,大家可以自己看看。

到此关于 ResultMapping 的解析和构建过程就分析完了,总的来说,还是比较复杂的。不过再难也是人写的,静下心都可以看懂。好了,其他就不多说了,继续往下分析。

2.1.3.2 解析 <constructor> 节点

一般情况下,我们所定义的实体类都是简单的 Java 对象,即 POJO。这种对象包含一些私有属性和相应的 getter/setter 方法,通常这种 POJO 可以满足大部分需求。但如果你想使用不可变类存储查询结果,则就需要做一些改动。比如把 POJO 的 setter 方法移除,增加构造方法用于初始化成员变量。对于这种不可变的 Java 类,需要通过带有参数的构造方法进行初始化(反射也可以达到同样目的)。下面举个例子说明一下:

public class ArticleDO { // ... public ArticleDO(Integer id, String title, String content) { this.id = id; this.title = title; this.content = content; } // ... }

如上,ArticleDO 的构造方法对应的配置如下:

<constructor> <idArg column="id"/> <arg column="title"/> <arg column="content"/> </constructor>

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpszjz.html