下面我们看看mybatis如何解析resultMap标签
resultMap
mybatis会resultMap标签中的每个字段与列的映射封装成ResultMapping
对象。下面我们先看下ResultMapping对象(仅仅显示部分。)
public class ResultMapping {
private Configuration configuration;
private String property;
private String column;
private Class<?> javaType;
private JdbcType jdbcType;
private TypeHandler<?> typeHandler;
private String nestedResultMapId;
private String nestedQueryId;
private Set<String> notNullColumns;
private String columnPrefix;
private List<ResultFlag> flags;
private List<ResultMapping> composites;
private String resultSet;
private String foreignColumn;
private boolean lazy;
....
}
解析resultMap标签。主要是在XMLMapperBuilder.resultMapElements
方法中进行的下面我们看看这个方法
private void resultMapElements(List<XNode> list) throws Exception {
for (XNode resultMapNode : list) {
try {
resultMapElement(resultMapNode);
} catch (IncompleteElementException e) {
// ignore, it will be retried
}
}
}
因为我们可以在mapper.xml中定义多个resultMap标签。所以这个地方for循环进行逐个解析。
此时我们看到resultMapElements
方法中调用了resultMapElement
。下面我们看看resultMapElements
是怎么实现的:
private ResultMap resultMapElement(XNode resultMapNode) throws Exception {
return resultMapElement(resultMapNode, Collections.<ResultMapping>emptyList());
}
resultMapElement
方法什么也没做单纯调用了一个重载的resultMapElement
。下面我们看看重载的resultMapElement
方法
private ResultMap resultMapElement(XNode resultMapNode,
List<ResultMapping> additionalResultMappings) {
String id = resultMapNode.getStringAttribute("id", resultMapNode.getValueBasedIdentifier());
String tmp = resultMapNode.getStringAttribute("resultType",resultMapNode.getStringAttribute("javaType"));
String tmp1 = resultMapNode.getStringAttribute("ofType",tmp1);
String type = resultMapNode.getStringAttribute("type",tmp );
String extend = resultMapNode.getStringAttribute("extends");
Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
Class<?> typeClass = resolveClass(type);
Discriminator discriminator = null;
//-------------------------1----------------------------------------------
List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
resultMappings.addAll(additionalResultMappings);
List<XNode> resultChildren = resultMapNode.getChildren();
for (XNode resultChild : resultChildren) {
if ("constructor".equals(resultChild.getName())) {
processConstructorElement(resultChild, typeClass, resultMappings);
} else if ("discriminator".equals(resultChild.getName())) {
discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
} else {
List<ResultFlag> flags = new ArrayList<ResultFlag>();
if ("id".equals(resultChild.getName())) {
flags.add(ResultFlag.ID);
}
resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
}
}
ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend,
discriminator, resultMappings, autoMapping);
try {
return resultMapResolver.resolve();
} catch (IncompleteElementException e) {
configuration.addIncompleteResultMap(resultMapResolver);
throw e;
}
}
这个方法不光resultMap使用了,association和Collection标签也使用了这个方法,因此这个方法也要适配Collection和association标签。此时我们以resultMap标签为例子看他是怎么解析的。
在我打标记1的上面单存了获取了resultMap中的属性type,id,autoMappings,extends的属性值,接下来我们看看1下面的内容。
我们首先跳过 resultMappings.addAll(additionalResultMappings);
这行代码 主要是为Discriminator标签服务的。
下面讲解了解析resultMap中的各种标签。解析完各种标签最后调用 ResultMapResolver .resolve()
生成resultMap对象,下面我们看看这个方法
public ResultMap resolve() {
return assistant.addResultMap(this.id, this.type, this.extend, this.discriminator, this.resultMappings,
this.autoMapping);
}
这个方法什么也没有做,而是直接调用addResultMap
方法进行生成的。
public ResultMap addResultMap(String id,
Class<?> type,
String extend,
Discriminator discriminator,
List<ResultMapping> resultMappings,
Boolean autoMapping) {
id = applyCurrentNamespace(id, false);
extend = applyCurrentNamespace(extend, true);
if (extend != null) {
if (!configuration.hasResultMap(extend)) {
throw new IncompleteElementException("Could not find a parent resultmap with id '" + extend + "'");
}
ResultMap resultMap = configuration.getResultMap(extend);
List<ResultMapping> extendedResultMappings = new ArrayList<ResultMapping>(resultMap.getResultMappings());
extendedResultMappings.removeAll(resultMappings);
// Remove parent constructor if this resultMap declares a constructor.
//移除继承属性中的resultMapping属性
boolean declaresConstructor = false;
for (ResultMapping resultMapping : resultMappings) {
if (resultMapping.getFlags()
.contains(ResultFlag.CONSTRUCTOR)) {
declaresConstructor = true;
break;
}
}
if (declaresConstructor) {
Iterator<ResultMapping> extendedResultMappingsIter = extendedResultMappings.iterator();
while (extendedResultMappingsIter.hasNext()) {
if (extendedResultMappingsIter.next()
.getFlags()
.contains(ResultFlag.CONSTRUCTOR)) {
extendedResultMappingsIter.remove();
}
}
}
resultMappings.addAll(extendedResultMappings);
}
ResultMap resultMap = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping).discriminator(
discriminator)
.build();
configuration.addResultMap(resultMap);
return resultMap;
}
这个方法中主要解决的是extends继承问题。解决完继承后,利用ResultMap的Builder生成了ResultMap对象。
construction标签解析
construction标签主要用processConstructorElement
方法进行解析的,下面我们看看该方法:
<constructor>
<idArg column="" select="" resultMap="" javaType="" jdbcType="" typeHandler=""/>
<arg column="" ..../>
...
</constructor>
private void processConstructorElement(XNode resultChild,
Class<?> resultType,
List<ResultMapping> resultMappings) {
List<XNode> argChildren = resultChild.getChildren();
for (XNode argChild : argChildren) {
//用于在resultMap集合分类的时候做区分
List<ResultFlag> flags = new ArrayList<ResultFlag>();
flags.add(ResultFlag.CONSTRUCTOR);
if ("idArg".equals(argChild.getName())) {
flags.add(ResultFlag.ID);
}
resultMappings.add(buildResultMappingFromContext(argChild, resultType, flags));
}
}
从上面我们可以看出,获取construction下面所以得子标签。然后解析每个字标签把每个子标签映射成了ResultMapping
。下面我们看看mybatis是如何进行映射的;flags标签后面我们在说他什么用。
封装ResultMapping对象是在buildResultMappingFromContext
中完成的下面我们看看这个对象
private ResultMapping buildResultMappingFromContext(XNode context,
Class<?> resultType,
List<ResultFlag> flags) {
String 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", processNestedResultMappings(context,
Collections.emptyList()));
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 = ( Class<? extends TypeHandler<?>> ) resolveClass(
typeHandler);
JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum,
nestedSelect, nestedResultMap, notNullColumn, columnPrefix,
typeHandlerClass, flags, resultSet, foreignColumn, lazy);
}
下面我们看看MapperBuilderAssistant.buildResultMapping
方法做了什么
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) {
//mybatis完成自动类型推断
Class<?> javaTypeClass = resolveResultJavaType(resultType, property, javaType);
TypeHandler<?> typeHandlerInstance = resolveTypeHandler(javaTypeClass, typeHandler);
List<ResultMapping> composites = parseCompositeColumnName(column);
if (composites.size() > 0) {
column = null;
}
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();
}
resolveResultJavaType
主要是自动类型推断,平常我们都没有写过javaType,这个方法完成了给javaType的赋值,也就是自动类型推断。他是通过我们设置的property属性,获取类的set+property方法的入参完成的自动类型推断。
resolveTypeHandler
如果我们不设置TypeHandler这个方法毫无作用,但是我们设置了TypeHandler这个方法主要是对类型处理器实例化,我们使用的类型处理器没有在TypeHandler标签中配置也可以进行实例化。
parseCompositeColumnName
这个方法主要是处理组合列的,组合列使用使用在collection标签中的column属性中,方便往collection中的select属性中传入参数。例如:
<collection column = '{p1=c1,p2=c2}'/>
最后调用builder
生成了映射关系,在该方法中也完成了TypeHandler的实例化。
在builder
中生成了一个ResultMapping对象。完成了一个列和一个java字段属性的映射。
id、result标签的解析
和construction标签解析一样,都会通过buildResultMappingFromContext
方法进行解析。最后生成了ResultMapping对象。
collection、association标签的解析
在buildResultMappingFromContext
方法中有那么一行代码context.getStringAttribute("resultMap", processNestedResultMappings(context,Collections.emptyList()))
这行代码。这行代码是干嘛呢。
主要是为了解析collection等标签中的id、result等标签。例如:
<collection property = '' ... >
<id ... />
<result ....>
</collection>
下面我们看看这行代码
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 = resultMapElement(context, resultMappings);
return resultMap.getId();
}
}
return null;
}
所以resultMap标签的解析大体上是从resultMapElement
到buildResultMappingFromContext
。把每个标签解析成resultMapping对象,然后把所有的resultMapping放入ResultMap中放入Configuration。