XMLMapperBuilder###parameterMapElement()
//代碼比較長了,因為parameterMap 涉及到比較多的東西
// <resultMap id="userMap" type="com.test.demo.model.SysUser">
// <id property="id" column="id"/>
// <result property="userPassword" column="user_password"/>
// <result property="userName" column="user_name"/>
// <result property="createTime" column="create_time" jdbcType="TIMESTAMP"/>
// </resultMap>
private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings, Class<?> enclosingType) throws Exception {
//首先記錄一下跟蹤日志,有關(guān)ErrorContext后面會詳細(xì)明說
ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
//這里可以看到用了一連串的嵌套,其實就是設(shè)置默認(rèn)值而已
//簡單解釋這里的操作就是:首先獲取`type`的值
//如果`type`為空就取`ofType`的值
//如果`ofType`為空就取`resultType`的值
//如果`resultType`為空就取`javaType`的值
String type = resultMapNode.getStringAttribute("type",
resultMapNode.getStringAttribute("ofType",
resultMapNode.getStringAttribute("resultType",
resultMapNode.getStringAttribute("javaType"))));
//解析這個類
Class<?> typeClass = resolveClass(type);
//如果type為null
//一般resolveClass只有在type為null的時候才會返回null
if (typeClass == null) {
//這里暫時沒看懂,因為解析`ResultMap`傳入的enclosingType為null
typeClass = inheritEnclosingType(resultMapNode, enclosingType);
}
Discriminator discriminator = null;
List<ResultMapping> resultMappings = new ArrayList<>();
resultMappings.addAll(additionalResultMappings);
//解析子節(jié)點
List<XNode> resultChildren = resultMapNode.getChildren();
for (XNode resultChild : resultChildren) {
//單獨處理構(gòu)造函數(shù)節(jié)點
if ("constructor".equals(resultChild.getName())) {
//處理構(gòu)造函數(shù)節(jié)點
processConstructorElement(resultChild, typeClass, resultMappings);
//單獨處理鑒定器節(jié)點
} else if ("discriminator".equals(resultChild.getName())) {
discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
} else {
List<ResultFlag> flags = new ArrayList<>();
//如果子節(jié)點為<id>,則將其保存起來,后續(xù)用來充當(dāng)equals的作用
if ("id".equals(resultChild.getName())) {
flags.add(ResultFlag.ID);
}
//處理其他的子節(jié)點
resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
}
}
//獲取此節(jié)點的id
String id = resultMapNode.getStringAttribute("id",
//不存在則自動生成一個唯一id
resultMapNode.getValueBasedIdentifier());
//獲取繼承節(jié)點信息
String extend = resultMapNode.getStringAttribute("extends");
//是否自動映射
Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
//創(chuàng)建解析類
ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
try {
//進行解析
return resultMapResolver.resolve();
} catch (IncompleteElementException e) {
configuration.addIncompleteResultMap(resultMapResolver);
throw e;
}
}
說實話,這段代碼是在比較難理解,就光前面的嵌套獲取type \ ofType \ jdbcType 等找了十萬遍都沒找到為什么。。甚至想到了是不是兼容老版本的問題。。可以看提交記錄這些代碼9年前都有了。。。
太難了
后來配合官方文檔看,也能大概明白:
<resultMap id="detailedBlogResultMap" type="Blog">
<constructor>
<idArg column="blog_id" javaType="int"/>
</constructor>
<result property="title" column="blog_title"/>
<association property="author" javaType="Author">
<id property="id" column="author_id"/>
<result property="username" column="author_username"/>
<result property="password" column="author_password"/>
<result property="email" column="author_email"/>
<result property="bio" column="author_bio"/>
<result property="favouriteSection" column="author_favourite_section"/>
</association>
<collection property="posts" ofType="Post">
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>
<association property="author" javaType="Author"/>
<collection property="comments" ofType="Comment">
<id property="id" column="comment_id"/>
</collection>
<collection property="tags" ofType="Tag" >
<id property="id" column="tag_id"/>
</collection>
<discriminator javaType="int" column="draft">
<case value="1" resultType="DraftPost"/>
</discriminator>
</collection>
</resultMap>
可以發(fā)現(xiàn)resultMap中可以嵌套<constructor>或者<collection>
而<collection>中又可以嵌套其他的,比如<association> \ <collection>
那如果要完整的解析這些嵌套的東西,最好的辦法就是遞歸,
這就是為什么前面會有3中type的獲取,因為這個方法不僅僅是在解析<resultMap>,還會被遞歸調(diào)用來解析<association> \ <collection> 等等。
先跳過其他的代碼,我們先看看解析其他子節(jié)點
XMLMapperBuilder###buildResultMappingFromContext()
private ResultMapping buildResultMappingFromContext(XNode context, Class<?> resultType, List<ResultFlag> flags) throws Exception {
String property;
//判斷時候需要通過構(gòu)造方法賦值
if (flags.contains(ResultFlag.CONSTRUCTOR)) {
property = context.getStringAttribute("name");
} else {
//獲取屬性
property = context.getStringAttribute("property");
}
//獲取其他屬性
String column = context.getStringAttribute("column");
String javaType = context.getStringAttribute("javaType");
String jdbcType = context.getStringAttribute("jdbcType");
String nestedSelect = context.getStringAttribute("select");
String nestedResultMap = context.getStringAttribute("resultMap",
//處理嵌套的resultMap
processNestedResultMappings(context, Collections.emptyList(), resultType));
//繼續(xù)獲取其他的屬性
String notNullColumn = context.getStringAttribute("notNullColumn");
String columnPrefix = context.getStringAttribute("columnPrefix");
String typeHandler = context.getStringAttribute("typeHandler");
String resultSet = context.getStringAttribute("resultSet");
String foreignColumn = context.getStringAttribute("foreignColumn");
boolean lazy = "lazy".equals(context.getStringAttribute("fetchType", configuration.isLazyLoadingEnabled() ? "lazy" : "eager"));
Class<?> javaTypeClass = resolveClass(javaType);
Class<? extends TypeHandler<?>> typeHandlerClass = resolveClass(typeHandler);
JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
//將這些屬性通過解析助手進行解析
return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, nestedSelect, nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resultSet, foreignColumn, lazy);
}
這里最重要的便是處理嵌套的resultMap
一般來說,能被嵌套的元素有:
XMLMapperBuilder###processNestedResultMappings()
private String processNestedResultMappings(XNode context, List<ResultMapping> resultMappings, Class<?> enclosingType) throws Exception {
if ("association".equals(context.getName())
|| "collection".equals(context.getName())
|| "case".equals(context.getName())) {
//先不解析動態(tài)SQL
if (context.getStringAttribute("select") == null) {
//驗證collection節(jié)點是否包含必須要元素
//必須包含resultMap 和 javaType其中一個
validateCollection(context, enclosingType);
//調(diào)用最開始的方法,進行遞歸解析
ResultMap resultMap = resultMapElement(context, resultMappings, enclosingType);
return resultMap.getId();
}
}
return null;
}
這里就能能看出,已經(jīng)開始遞歸調(diào)用了
接下來,我們以<association> / <collection>為主體元素,再次分析一遍resultMapElement的源代碼
XMLMapperBuilder###parameterMapElement()
private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings, Class<?> enclosingType) throws Exception {
ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
//對于resultMap 獲取 type
//對于collection 獲取 ofType 或 javaType
//對于association 獲取 javaType
String type = resultMapNode.getStringAttribute("type",
resultMapNode.getStringAttribute("ofType",
resultMapNode.getStringAttribute("resultType",
resultMapNode.getStringAttribute("javaType"))));
//加載此type,可以指定別名
Class<?> typeClass = resolveClass(type);
if (typeClass == null) {
//這里enclosingType一般都是父節(jié)點的type
//比如<resultMap type="test">
// <collection/>
//那么enclosingType便是test
//如果有些節(jié)點沒有配置type,允許的情況下,可以直接使用父節(jié)點的type
//例如:
// <discriminator javaType="int" column="draft">
// <case value="1" resultType="DraftPost"/>
// </discriminator>
// case節(jié)點中就沒有配置type
typeClass = inheritEnclosingType(resultMapNode, enclosingType);
}
Discriminator discriminator = null;
List<ResultMapping> resultMappings = new ArrayList<>();
//添加已經(jīng)解析過的resultMap
resultMappings.addAll(additionalResultMappings);
//繼續(xù)解析子節(jié)點
List<XNode> resultChildren = resultMapNode.getChildren();
for (XNode resultChild : resultChildren) {
//處理構(gòu)造方法
if ("constructor".equals(resultChild.getName())) {
processConstructorElement(resultChild, typeClass, resultMappings);
}
//處理鑒定器
else if ("discriminator".equals(resultChild.getName())) {
discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
} else {
//處理其他子節(jié)點
List<ResultFlag> flags = new ArrayList<>();
if ("id".equals(resultChild.getName())) {
flags.add(ResultFlag.ID);
}
resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
}
}
//獲取id
String id = resultMapNode.getStringAttribute("id",
resultMapNode.getValueBasedIdentifier());
String extend = resultMapNode.getStringAttribute("extends");
Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
//構(gòu)建resultMap
ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
try {
//最后的解析
return resultMapResolver.resolve();
} catch (IncompleteElementException e) {
configuration.addIncompleteResultMap(resultMapResolver);
throw e;
}
}
接下來看其他細(xì)節(jié):
當(dāng)子節(jié)點解析完了以后,會將子節(jié)點添加到resultMapping中,然后再解析最大的resultMap
MapperBuilderAssiant###addResultMap()
//resultMapResolver.resolve();內(nèi)部調(diào)用的此方法
public ResultMap addResultMap(
String id,
Class<?> type,
String extend,
Discriminator discriminator,
List<ResultMapping> resultMappings,
Boolean autoMapping) {
//首先給當(dāng)前標(biāo)簽加上命名空間前綴
id = applyCurrentNamespace(id, false);
//然后給繼承的標(biāo)簽加上命名空間前綴
//從這個當(dāng)前前綴可以看出來,繼承只能繼承當(dāng)前命名空間的元素
extend = applyCurrentNamespace(extend, true);
//判斷是否有集成的屬性
if (extend != null) {
//如果所繼承的屬性還沒有解析,那么拋出指定異常,稍后再解析
if (!configuration.hasResultMap(extend)) {
throw new IncompleteElementException("Could not find a parent resultmap with id '" + extend + "'");
}
//通過繼承找到對應(yīng)的resultMap
ResultMap resultMap = configuration.getResultMap(extend);
//獲取這個resultMap id 下對應(yīng)的所有resultMap (包括嵌套resultMap)
List<ResultMapping> extendedResultMappings = new ArrayList<>(resultMap.getResultMappings());
//去除重復(fù)resultMap
extendedResultMappings.removeAll(resultMappings);
// Remove parent constructor if this resultMap declares a constructor.
boolean declaresConstructor = false;
//檢查是否需要使用帶參構(gòu)造方法構(gòu)造resultMap
for (ResultMapping resultMapping : resultMappings) {
if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {
declaresConstructor = true;
break;
}
}
//如果需要使用構(gòu)造方法創(chuàng)鍵`resultMap type`,那么將此需要使用構(gòu)造方法的元素從繼承元素中刪除
if (declaresConstructor) {
extendedResultMappings.removeIf(resultMapping -> resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR));
}
resultMappings.addAll(extendedResultMappings);
}
//構(gòu)造resultMap
ResultMap resultMap = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping)
.discriminator(discriminator)
.build();
configuration.addResultMap(resultMap);
return resultMap;
}
這幾段代碼看著實在累,第一是比較復(fù)雜,第二個就是本來想要實現(xiàn)的功能也比較復(fù)雜。
這里可以看見MyBatis僅僅是將嵌套的resultMap分解為幾個reusltMap,然后放入list中,并且這里解析配置就真正的只是解析配置,沒有做任何多余的事、
同時可以看出來,MyBatis的模塊劃分是非常好的,
XMLConfigBuilder->XMLMapperBuilder->MapperBuilderAssistant
XMLMapperBuilder只用負(fù)責(zé)讀取配置文件,而將配置文件生成對像則交給MapperBuilderAssistant
看上面的代碼,resultMap也只解析了靜態(tài)部分能夠解析的地方,而需要動態(tài)生成的則直接原封不動的放入了Configuration類中