1. 概述
本文接 《精尽 MyBatis 源码分析 —— MyBatis 初始化(一)之加载 mybatis-config》 一文,来分享 MyBatis 初始化的第二步,加载 Mapper 映射配置文件。而这个步骤的入口是 XMLMapperBuilder 。下面,我们一起来看看它的代码实现。
- 上图,就是 Mapper 映射配置文件的解析结果。
2. XMLMapperBuilder
org.apache.ibatis.builder.xml.XMLMapperBuilder
,继承 BaseBuilder 抽象类,Mapper XML 配置构建器,主要负责解析 Mapper 映射配置文件。
2.1 构造方法
// XMLMapperBuilder.java
|
-
builderAssistant
属性,MapperBuilderAssistant 对象,是 XMLMapperBuilder 和 MapperAnnotationBuilder 的小助手,提供了一些公用的方法,例如创建 ParameterMap、MappedStatement 对象等等。关于 MapperBuilderAssistant 类,可见 「3. MapperBuilderAssistant」 。
2.2 parse
#parse()
方法,解析 Mapper XML 配置文件。代码如下:
// XMLMapperBuilder.java
|
-
<1>
处,调用Configuration#isResourceLoaded(String resource)
方法,判断当前 Mapper 是否已经加载过。代码如下:// Configuration.java
/**
* 已加载资源( Resource )集合
*/
protected final Set<String> loadedResources = new HashSet<>();
public boolean isResourceLoaded(String resource) {
return loadedResources.contains(resource);
} -
<3>
处,调用Configuration#addLoadedResource(String resource)
方法,标记该 Mapper 已经加载过。代码如下:// Configuration.java
public void addLoadedResource(String resource) {
loadedResources.add(resource);
} -
<2>
处,调用#configurationElement(XNode context)
方法,解析<mapper />
节点。详细解析,见 「2.3 configurationElement」 。 -
<4>
处,调用#bindMapperForNamespace()
方法,绑定 Mapper 。详细解析, -
<5>
、<6>
、<7>
处,解析对应的待定的节点。详细解析,见 「2.5 parsePendingXXX」 。
2.3 configurationElement
#configurationElement(XNode context)
方法,解析 <mapper />
节点。代码如下:
// XMLMapperBuilder.java
|
-
<1>
处,获得namespace
属性,并设置到 MapperAnnotationBuilder 中。 -
<2>
处,调用#cacheRefElement(XNode context)
方法,解析<cache-ref />
节点。详细解析,见 「2.3.1 cacheElement」 。 -
<3>
处,调用#cacheElement(XNode context)
方法,解析cache />
标签。详细解析,见 「2.3.2 cacheElement」 。 -
<4>
处,调用#resultMapElements(List<XNode> list)
方法,解析<resultMap />
节点们。详细解析,见 「2.3.3 resultMapElements」 。 -
<5>
处,调用#sqlElement(List<XNode> list)
方法,解析<sql />
节点们。详细解析,见 「2.3.4 sqlElement」 。 -
<6>
处,调用#buildStatementFromContext(List<XNode> list)
方法,解析<select />
、<insert />
、<update />
、<delete />
节点们。详细解析,见 「2.3.5 buildStatementFromContext」 。
2.3.1 cacheElement
#cacheRefElement(XNode context)
方法,解析 <cache-ref />
节点。代码如下:
// XMLMapperBuilder.java
|
-
示例如下:
<cache-ref namespace="com.someone.application.data.SomeMapper"/>
-
<1>
处,获得指向的namespace
名字,并调用Configuration#addCacheRef(String namespace, String referencedNamespace)
方法,添加到configuration
的cacheRefMap
中。代码如下:// Configuration.java
/**
* A map holds cache-ref relationship. The key is the namespace that
* references a cache bound to another namespace and the value is the
* namespace which the actual cache is bound to.
*
* Cache 指向的映射
*
* @see #addCacheRef(String, String)
* @see org.apache.ibatis.builder.xml.XMLMapperBuilder#cacheRefElement(XNode)
*/
protected final Map<String, String> cacheRefMap = new HashMap<>();
public void addCacheRef(String namespace, String referencedNamespace) {
cacheRefMap.put(namespace, referencedNamespace);
} -
<2>
处,创建 CacheRefResolver 对象,并调用CacheRefResolver#resolveCacheRef()
方法,执行解析。关于 CacheRefResolver ,在 「2.3.1.1 CacheRefResolver」 详细解析。 -
<3>
处,解析失败,因为此处指向的 Cache 对象可能未初始化,则先调用Configuration#addIncompleteCacheRef(CacheRefResolver incompleteCacheRef)
方法,添加到configuration
的incompleteCacheRefs
中。代码如下:// Configuration.java
/**
* CacheRefResolver 集合
*/
protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<>();
public void addIncompleteCacheRef(CacheRefResolver incompleteCacheRef) {
incompleteCacheRefs.add(incompleteCacheRef);
}
2.3.1.1 CacheRefResolver
org.apache.ibatis.builder.CacheRefResolver
,Cache 指向解析器。代码如下:
// CacheRefResolver.java
|
- 在
#resolveCacheRef()
方法中,会调用MapperBuilderAssistant#useCacheRef(String namespace)
方法,获得指向的 Cache 对象。详细解析,见 「3.3 useCacheRef」 。
2.3.2 cacheElement
#cacheElement(XNode context)
方法,解析 cache />
标签。代码如下:
// XMLMapperBuilder.java
|
-
示例如下:
// 使用默认缓存
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
// 使用自定义缓存
<cache type="com.domain.something.MyCustomCache">
<property name="cacheFile" value="/tmp/my-custom-cache.tmp"/>
</cache> -
<1>
、<2>
、<3>
、<4>
处,见代码注释即可。 -
<5>
处,调用MapperBuilderAssistant#useNewCache(...)
方法,创建 Cache 对象。详细解析,见 「3.4 useNewCache」 中。
2.3.3 resultMapElements
老艿艿:开始高能,保持耐心。
整体流程如下:
#resultMapElements(List<XNode> list)
方法,解析 <resultMap />
节点们。代码如下:
// XMLMapperBuilder.java
|
-
<resultMap />
标签的解析,是相对复杂的过程,情况比较多,所以胖友碰到不懂的,可以看看 《MyBatis 文档 —— Mapper XML 文件》 文档。 -
<1>
处,获得id
、type
、extends
、autoMapping
属性,并解析type
对应的类型。 -
<2>
处,创建 ResultMapping 集合,后遍历<resultMap />
的子节点们,将每一个子节点解析成一个或多个 ResultMapping 对象,添加到集合中。即如下图所示:-
<2.1>
处,调用#processConstructorElement(...)
方法,处理<constructor />
节点。详细解析,见 「2.3.3.1 processConstructorElement」 。 -
<2.2>
处,调用#processDiscriminatorElement(...)
方法,处理<discriminator />
节点。详细解析,见 「2.3.3.2 processDiscriminatorElement」 。 -
<2.3>
处,调用#buildResultMappingFromContext(XNode context, Class<?> resultType, List<ResultFlag> flags)
方法,将当前子节点构建成 ResultMapping 对象,并添加到resultMappings
中。详细解析,见 「2.3.3.3 buildResultMappingFromContext」 。?? 这一块,和 「2.3.3.1 processConstructorElement」 的<3>
是一致的。
-
-
<3>
处,创建 ResultMapResolver 对象,执行解析。关于 ResultMapResolver ,在 「2.3.1.1 CacheRefResolver」 详细解析。 -
<4>
处,如果解析失败,说明有依赖的信息不全,所以调用Configuration#addIncompleteResultMap(ResultMapResolver resultMapResolver)
方法,添加到 Configuration 的incompleteResultMaps
中。代码如下:// Configuration.java
/**
* ResultMapResolver 集合
*/
protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<>();
public void addIncompleteResultMap(ResultMapResolver resultMapResolver) {
incompleteResultMaps.add(resultMapResolver);
}
2.3.3.1 processConstructorElement
#processConstructorElement(XNode resultChild, Class<?> resultType, List<ResultMapping> resultMappings)
方法,处理 <constructor />
节点。代码如下:
// XMLMapperBuilder.java
|
-
<1>
和<3>
处,遍历<constructor />
的子节点们,调用#buildResultMappingFromContext(XNode context, Class<?> resultType, List<ResultFlag> flags)
方法,将当前子节点构建成 ResultMapping 对象,并添加到resultMappings
中。详细解析,见 「2.3.3.3 buildResultMappingFromContext」 。 -
<2>
处,我们可以看到一个org.apache.ibatis.mapping.ResultFlag
枚举类,结果标识。代码如下:// ResultFlag.java
public enum ResultFlag {
/**
* ID
*/
ID,
/**
* 构造方法
*/
CONSTRUCTOR
}- 具体的用途,见下文。
2.3.3.2 processDiscriminatorElement
#processDiscriminatorElement(XNode context, Class<?> resultType, List<ResultMapping> resultMappings)
方法,处理 <constructor />
节点。代码如下:
// XMLMapperBuilder.java
|
-
可能大家对
<discriminator />
标签不是很熟悉,可以打开 《MyBatis 文档 —— Mapper XML 文件》 文档,然后下【鉴别器】。?? 当然,这块简单了解下就好,实际场景下,艿艿貌似都不知道它的存在,哈哈哈哈。 -
<1>
处,解析各种属性以及属性对应的类。 -
<2>
处,遍历<discriminator />
的子节点,解析成discriminatorMap
集合。 -
<2.1>
处,如果是内嵌的 ResultMap 的情况,则调用#processNestedResultMappings(XNode context, List<ResultMapping> resultMappings)
方法,处理内嵌的 ResultMap 的情况。代码如下:// XMLMapperBuilder.java
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) {
// 解析,并返回 ResultMap
ResultMap resultMap = resultMapElement(context, resultMappings);
return resultMap.getId();
}
}
return null;
}- 该方法,会“递归”调用
#resultMapElement(XNode context, List<ResultMapping> resultMappings)
方法,处理内嵌的 ResultMap 的情况。也就是返回到 「2.3.3 resultMapElement」 流程。
- 该方法,会“递归”调用
-
<3>
处,调用MapperBuilderAssistant#buildDiscriminator(...)
方法,创建 Discriminator 对象。详细解析,见 「3.6 buildDiscriminator」 。
2.3.3.3 buildResultMappingFromContext
#buildResultMappingFromContext(XNode context, Class<?> resultType, List<ResultFlag> flags)
方法,将当前节点构建成 ResultMapping 对象。代码如下:
// XMLMapperBuilder.java
|
-
<1>
处,解析各种属性以及属性对应的类。 -
<2>
处,调用MapperBuilderAssistant#buildResultMapping(...)
方法,构建 ResultMapping 对象。详细解析,见 「3.5 buildResultMapping」 。
2.3.3.4 ResultMapResolver
org.apache.ibatis.builder.ResultMapResolver
,ResultMap 解析器。代码如下:
// ResultMapResolver.java
|
- 在
#resolve()
方法中,会调用MapperBuilderAssistant#addResultMap(...)
方法,创建 ResultMap 对象。详细解析,见 「3.7 addResultMap」 。
2.3.4 sqlElement
#sqlElement(List<XNode> list)
方法,解析 <sql />
节点们。代码如下:
// XMLMapperBuilder.java
|
-
<1>
处,遍历所有<sql />
节点,逐个处理。 -
<2>
处,获得databaseId
属性。 -
<3>
处,获得完整的id
属性,格式为${namespace}.${id}
。 -
<4>
处,调用#databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId)
方法,判断databaseId
是否匹配。代码如下:// XMLMapperBuilder.java
private boolean databaseIdMatchesCurrent(String id, String databaseId, String requiredDatabaseId) {
// 如果不匹配,则返回 false
if (requiredDatabaseId != null) {
return requiredDatabaseId.equals(databaseId);
} else {
// 如果未设置 requiredDatabaseId ,但是 databaseId 存在,说明还是不匹配,则返回 false
// mmp ,写的好绕
if (databaseId != null) {
return false;
}
// skip this fragment if there is a previous one with a not null databaseId
// 判断是否已经存在
if (this.sqlFragments.containsKey(id)) {
XNode context = this.sqlFragments.get(id);
// 若存在,则判断原有的 sqlFragment 是否 databaseId 为空。因为,当前 databaseId 为空,这样两者才能匹配。
return context.getStringAttribute("databaseId") == null;
}
}
return true;
} -
<5>
处,添加到sqlFragments
中。因为sqlFragments
是来自 Configuration 的sqlFragments
属性,所以相当于也被添加了。代码如下:// Configuration.java
/**
* 可被其他语句引用的可重用语句块的集合
*
* 例如:<sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>
*/
protected final Map<String, XNode> sqlFragments = new StrictMap<>("XML fragments parsed from previous mappers");
2.3.5 buildStatementFromContext
#buildStatementFromContext(List<XNode> list)
方法,解析 <select />
、<insert />
、<update />
、<delete />
节点们。代码如下:
// XMLMapperBuilder.java
|
-
<1>
处,遍历<select />
、<insert />
、<update />
、<delete />
节点们,逐个创建 XMLStatementBuilder 对象,执行解析。关于 XMLStatementBuilder 类,我们放在下篇文章,详细解析。 -
<2>
处,解析失败,调用Configuration#addIncompleteStatement(XMLStatementBuilder incompleteStatement)
方法,添加到configuration
中。代码如下:// Configuration.java
/**
* XMLStatementBuilder 集合
*/
protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<>();
public void addIncompleteStatement(XMLStatementBuilder incompleteStatement) {
incompleteStatements.add(incompleteStatement);
}
2.4 bindMapperForNamespace
#bindMapperForNamespace()
方法,绑定 Mapper 。代码如下:
// XMLMapperBuilder.java
|
-
<1>
处,获得 Mapper 映射配置文件对应的 Mapper 接口,实际上类名就是namespace
。嘿嘿,这个是常识。 -
<2>
处,调用Configuration#hasMapper(Class<?> type)
方法,判断若谷不存在该 Mapper 接口,则进行绑定。代码如下:// Configuration.java
/**
* MapperRegistry 对象
*/
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
public boolean hasMapper(Class<?> type) {
return mapperRegistry.hasMapper(type);
} -
<3>
处,调用Configuration#addLoadedResource(String resource)
方法,标记namespace
已经添加,避免MapperAnnotationBuilder#loadXmlResource(...)
重复加载。代码如下:// MapperAnnotationBuilder.java
private void loadXmlResource() {
// Spring may not know the real resource name so we check a flag
// to prevent loading again a resource twice
// this flag is set at XMLMapperBuilder#bindMapperForNamespace
if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
// ... 省略创建 XMLMapperBuilder ,进行解析的代码
}
} -
<4>
处,调用Configuration#addMapper(Class<T> type)
方法,添加到configuration
的mapperRegistry
中。代码如下:// Configuration.java
public <T> void addMapper(Class<T> type) {
mapperRegistry.addMapper(type);
}
2.5 parsePendingXXX
有三个 parsePendingXXX 方法,代码如下:
// XMLMapperBuilder.java
|
- 三个方法的逻辑思路基本一致:1)获得对应的集合;2)遍历集合,执行解析;3)执行成功,则移除出集合;4)执行失败,忽略异常。
- 当然,实际上,此处还是可能有执行解析失败的情况,但是随着每一个 Mapper 配置文件对应的 XMLMapperBuilder 执行一次这些方法,逐步逐步就会被全部解析完。??
3. MapperBuilderAssistant
org.apache.ibatis.builder.MapperBuilderAssistant
,继承 BaseBuilder 抽象类,Mapper 构造器的小助手,提供了一些公用的方法,例如创建 ParameterMap、MappedStatement 对象等等。
3.1 构造方法
// MapperBuilderAssistant.java
|
- 实际上,?? 如果要不是为了 XMLMapperBuilder 和 MapperAnnotationBuilder 都能调用到这个公用方法,可能都不需要这个类。
3.2 setCurrentNamespace
#setCurrentNamespace(String currentNamespace)
方法,设置 currentNamespace
属性。代码如下:
// MapperBuilderAssistant.java
|
3.3 useCacheRef
#useCacheRef(String namespace)
方法,获得指向的 Cache 对象。如果获得不到,则抛出 IncompleteElementException 异常。代码如下:
// MapperBuilderAssistant.java
|
-
<1>
处,调用Configuration#getCache(String id)
方法,获得 Cache 对象。代码如下:// Configuration.java
/**
* Cache 对象集合
*
* KEY:命名空间 namespace
*/
protected final Map<String, Cache> caches = new StrictMap<>("Caches collection");
public Cache getCache(String id) {
return caches.get(id);
}
3.4 useNewCache
#useNewCache(Class<? extends Cache> typeClass, Class<? extends Cache> evictionClass, Long flushInterval, Integer size, boolean readWrite, boolean blocking, Properties props)
方法,创建 Cache 对象。代码如下:
// MapperBuilderAssistant.java
|
-
<1>
处,创建 Cache 对象。关于 CacheBuilder 类,详细解析,见 「3.4.1 CacheBuilder」 。 -
<2>
处,调用Configuration#addCache(Cache cache)
方法,添加到configuration
的caches
中。代码如下:// Configuration.java
public void addCache(Cache cache) {
caches.put(cache.getId(), cache);
} -
<3>
处,赋值给currentCache
。
3.4.1 CacheBuilder
org.apache.ibatis.mapping.CacheBuilder
,Cache 构造器。基于装饰者设计模式,进行 Cache 对象的构造。代码比较简单,但是有点略长,胖友直接点击 链接 查看,已经添加了完整的注释。
3.5 buildResultMapping
#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)
方法,构造 ResultMapping 对象。代码如下:
// MapperBuilderAssistant.java
|
-
<1>
处,解析对应的 Java Type 类和 TypeHandler 对象。 -
<2>
处,调用#parseCompositeColumnName(String columnName)
方法,解析组合字段名称成 ResultMapping 集合。详细解析,见 「3.5.1 parseCompositeColumnName」 中。 -
<3>
处,创建 ResultMapping 对象。-
<3.1>
处,调用#applyCurrentNamespace(String base, boolean isReference)
方法,拼接命名空间。详细解析,见 「3.5.2 applyCurrentNamespace」 。 -
<3.2>
处,调用#parseMultipleColumnNames(String notNullColumn)
方法,将字符串解析成集合。详细解析,见 「3.5.3 parseMultipleColumnNames」 。 - 关于 ResultMapping 类,在 「3.5.4 ResultMapping」 中详细解析。
-
3.5.1 parseCompositeColumnName
#parseCompositeColumnName(String columnName)
方法,解析组合字段名称成 ResultMapping 集合。代码如下:
// MapperBuilderAssistant.java
|
-
对于这种情况,官方文档说明如下:
FROM 《MyBatis 文档 —— Mapper XML 文件》 的 「关联的嵌套查询」 小节
来自数据库的列名,或重命名的列标签。这和通常传递给 resultSet.getString(columnName)方法的字符串是相同的。 column 注 意 : 要 处 理 复 合 主 键 , 你 可 以 指 定 多 个 列 名 通 过 column= ” {prop1=col1,prop2=col2} ” 这种语法来传递给嵌套查询语 句。这会引起 prop1 和 prop2 以参数对象形式来设置给目标嵌套查询语句。
-
?? 不用理解太细,如果胖友和我一样,基本用不到这个特性。
3.5.2 applyCurrentNamespace
#applyCurrentNamespace(String base, boolean isReference)
方法,拼接命名空间。代码如下:
// MapperBuilderAssistant.java
|
- 通过这样的方式,生成唯一在的标识。
3.5.3 parseMultipleColumnNames
#parseMultipleColumnNames(String notNullColumn)
方法,将字符串解析成集合。代码如下:
// MapperBuilderAssistant.java
|
3.5.4 ResultMapping
org.apache.ibatis.mapping.ResultMapping
,ResultMap 中的每一条结果字段的映射。代码比较简单,但是有点略长,胖友直接点击 链接 查看,已经添加了完整的注释。
3.6 buildDiscriminator
#buildDiscriminator(Class<?> resultType, String column, Class<?> javaType, JdbcType jdbcType, Class<? extends TypeHandler<?>> typeHandler, Map<String, String> discriminatorMap)
方法,构建 Discriminator 对象。代码如下:
// MapperBuilderAssistant.java
|
- 简单看看就好,
<discriminator />
平时用的很少。
3.6.1 Discriminator
org.apache.ibatis.mapping.Discriminator
,鉴别器,代码比较简单,胖友直接点击 链接 查看,已经添加了完整的注释。
3.7 addResultMap
#addResultMap(String id, Class<?> type, String extend, Discriminator discriminator, List<ResultMapping> resultMappings, Boolean autoMapping)
方法,创建 ResultMap 对象,并添加到 Configuration 中。代码如下:
// MapperBuilderAssistant.java
|
-
<1>
处,获得 ResultMap 编号,即格式为${namespace}.${id}
。 -
<2.1>
处,获取完整的 extend 属性,即格式为${namespace}.${extend}
。从这里的逻辑来看,貌似只能自己 namespace 下的 ResultMap 。 -
<2.2>
处,如果有父类,则将父类的 ResultMap 集合,添加到resultMappings
中。逻辑有些绕,胖友耐心往下看。-
<2.2.1>
处,获得extend
对应的 ResultMap 对象。如果不存在,则抛出 IncompleteElementException 异常。代码如下:// Configuration.java
/**
* ResultMap 的映射
*
* KEY:`${namespace}.${id}`
*/
protected final Map<String, ResultMap> resultMaps = new StrictMap<>("Result Maps collection");
public ResultMap getResultMap(String id) {
return resultMaps.get(id);
}
public boolean hasResultMap(String id) {
return resultMaps.containsKey(id);
}- x
-
-
<3>
处,创建 ResultMap 对象。详细解析,见 「3.7.1 ResultMap」 。 -
<4>
处, 调用Configuration#addResultMap(ResultMap rm)
方法,添加到 Configuration 的resultMaps
中。代码如下:// Configuration.java
public void addResultMap(ResultMap rm) {
// <1> 添加到 resultMaps 中
resultMaps.put(rm.getId(), rm);
// 遍历全局的 ResultMap 集合,若其拥有 Discriminator 对象,则判断是否强制标记为有内嵌的 ResultMap
checkLocallyForDiscriminatedNestedResultMaps(rm);
// 若传入的 ResultMap 不存在内嵌 ResultMap 并且有 Discriminator ,则判断是否需要强制表位有内嵌的 ResultMap
checkGloballyForDiscriminatedNestedResultMaps(rm);
}-
<1>
处,添加到resultMaps
中。 -
<2>
处,调用#checkLocallyForDiscriminatedNestedResultMaps(ResultMap rm)
方法,遍历全局的 ResultMap 集合,若其拥有 Discriminator 对象,则判断是否强制标记为有内嵌的 ResultMap 。代码如下:// Configuration.java
// Slow but a one time cost. A better solution is welcome.
protected void checkGloballyForDiscriminatedNestedResultMaps(ResultMap rm) {
// 如果传入的 ResultMap 有内嵌的 ResultMap
if (rm.hasNestedResultMaps()) {
// 遍历全局的 ResultMap 集合
for (Map.Entry<String, ResultMap> entry : resultMaps.entrySet()) {
Object value = entry.getValue();
if (value != null) {
ResultMap entryResultMap = (ResultMap) value;
// 判断遍历的全局的 entryResultMap 不存在内嵌 ResultMap 并且有 Discriminator
if (!entryResultMap.hasNestedResultMaps() && entryResultMap.getDiscriminator() != null) {
// 判断是否 Discriminator 的 ResultMap 集合中,使用了传入的 ResultMap 。
// 如果是,则标记为有内嵌的 ResultMap
Collection<String> discriminatedResultMapNames = entryResultMap.getDiscriminator().getDiscriminatorMap().values();
if (discriminatedResultMapNames.contains(rm.getId())) {
entryResultMap.forceNestedResultMaps();
}
}
}
}
}
}- 逻辑有点绕,胖友耐心看下去。。。
-
<3>
处,调用#checkLocallyForDiscriminatedNestedResultMaps(ResultMap rm)
方法,若传入的 ResultMap 不存在内嵌 ResultMap 并且有 Discriminator ,则判断是否需要强制表位有内嵌的 ResultMap 。代码如下:// Configuration.java
// Slow but a one time cost. A better solution is welcome.
protected void checkLocallyForDiscriminatedNestedResultMaps(ResultMap rm) {
// 如果传入的 ResultMap 不存在内嵌 ResultMap 并且有 Discriminator
if (!rm.hasNestedResultMaps() && rm.getDiscriminator() != null) {
// 遍历传入的 ResultMap 的 Discriminator 的 ResultMap 集合
for (Map.Entry<String, String> entry : rm.getDiscriminator().getDiscriminatorMap().entrySet()) {
String discriminatedResultMapName = entry.getValue();
if (hasResultMap(discriminatedResultMapName)) {
// 如果引用的 ResultMap 存在内嵌 ResultMap ,则标记传入的 ResultMap 存在内嵌 ResultMap
ResultMap discriminatedResultMap = resultMaps.get(discriminatedResultMapName);
if (discriminatedResultMap.hasNestedResultMaps()) {
rm.forceNestedResultMaps();
break;
}
}
}
}
}- 逻辑有点绕,胖友耐心看下去。。。整体逻辑,和
#checkGloballyForDiscriminatedNestedResultMaps(ResultMap rm)
方法是类似的,互为“倒影”。
- 逻辑有点绕,胖友耐心看下去。。。整体逻辑,和
-
3.7.1 ResultMap
org.apache.ibatis.mapping.ResultMap
,结果集,例如 <resultMap />
解析后的对象。代码比较简单,但是有点略长,胖友直接点击 链接 查看,已经添加了完整的注释。