2.2 解析resultMap标签

下面我们看看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标签的解析大体上是从resultMapElementbuildResultMappingFromContext。把每个标签解析成resultMapping对象,然后把所有的resultMapping放入ResultMap中放入Configuration。

  • 20
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值