Mybatis3源码分析(04)-加载Configuration-XMLMapperBuilder加载ResultMap
来源:互联网 发布:红米note3root软件 编辑:程序博客网 时间:2024/05/16 15:33
XMLMapperBuilder.parse()方法
XMLConfigBuilder在解析过程中碰到mappers元素会交由XMLMapperBuilder.parse()方法来处理
public void parse() { if (!configuration.isResourceLoaded(resource)) { //真正解析 configurationElement(parser.evalNode("/mapper")); configuration.addLoadedResource(resource); bindMapperForNamespace(); } parsePendingResultMaps(); parsePendingChacheRefs(); parsePendingStatements(); }
private void configurationElement(XNode context) { try { String namespace = context.getStringAttribute("namespace"); //在Mybatis3中mapper元素必须定义命名空间 if (namespace.equals("")) { throw new BuilderException("Mapper's namespace cannot be empty"); } builderAssistant.setCurrentNamespace(namespace); //缓存,以后再看 cacheRefElement(context.evalNode("cache-ref")); cacheElement(context.evalNode("cache")); //解析parameterMap节点,Mybatis3中官方已经不推荐使用parameterMap配置,这里也不做讨论 parameterMapElement(context.evalNodes("/mapper/parameterMap")); //解析resultMap resultMapElements(context.evalNodes("/mapper/resultMap")); sqlElement(context.evalNodes("/mapper/sql")); //解析sql buildStatementFromContext(context.evalNodes("select|insert|update|delete")); } catch (Exception e) { throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e); } }
解析resultMap节点
private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception { ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier()); //id,对应ResultMap.id,内映射没有ID,用<span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px;">resultMapNode.getValueBasedIdentifier()方法生成一个</span> String id = resultMapNode.getStringAttribute("id", resultMapNode.getValueBasedIdentifier()); //type,对应ResultMap.Type,这里可以看到这个type可以通过很多个属性进行配置 String type = resultMapNode.getStringAttribute("type", resultMapNode.getStringAttribute("ofType", resultMapNode.getStringAttribute("resultType", resultMapNode.getStringAttribute("javaType")))); String extend = resultMapNode.getStringAttribute("extends"); //是否自动映射 Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping"); //将type解析成class,可以是别名,也可以是全限定名 Class<?> typeClass = resolveClass(type); Discriminator discriminator = null; //这个resultMappings将对应ResultMap.resultMappings List<ResultMapping> resultMappings = new ArrayList<ResultMapping>(); resultMappings.addAll(additionalResultMappings); List<XNode> resultChildren = resultMapNode.getChildren(); for (XNode resultChild : resultChildren) {//解析子节点 if ("constructor".equals(resultChild.getName())) { //解析constructor节点 processConstructorElement(resultChild, typeClass, resultMappings); } else if ("discriminator".equals(resultChild.getName())) { //解析disriminator节点,暂时不讨论 discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings); } else { ArrayList<ResultFlag> flags = new ArrayList<ResultFlag>(); if ("id".equals(resultChild.getName())) { flags.add(ResultFlag.ID); } //解析其他节点,主要有result、association及collection //在这里可以说明一个result、association及collection都会被解析成一个resultMapping对象,即使他们有很多子元素 resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags)); } } ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping); try { //得到resultMappings后,生成ResultMap并加入到Configuration中 return resultMapResolver.resolve(); } catch (IncompleteElementException e) { configuration.addIncompleteResultMap(resultMapResolver); throw e; } }resultMapResolver.resolve()方法
public ResultMap resolve() { return assistant.addResultMap(this.id, this.type, this.extend, this.discriminator, this.resultMappings, this.autoMapping); }
MapperBuilderAssistant.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); //交由ResultMap.Builder来创建ResultMap对象,ResultMap.Builder.build()方法前面已经介绍过 ResultMap.Builder resultMapBuilder = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping); if (extend != null) { //对继承了其他resultMap的处理,暂时不讨论 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. 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); } resultMapBuilder.discriminator(discriminator); //生成ResultMap对象 ResultMap resultMap = resultMapBuilder.build(); //另到Configuration中 configuration.addResultMap(resultMap); return resultMap; }
经过以上分析,在得到resultMappings之后再构造ResultMap对象的过程已经完成。下面再看看ResultMapping是怎么被解析的,也就是ResultMap的直接子节点的解析。
ResultMapping解析
constructor节点解析
private void processConstructorElement(XNode resultChild, Class<?> resultType, List<ResultMapping> resultMappings) throws Exception { List<XNode> argChildren = resultChild.getChildren(); //遍历子节点 for (XNode argChild : argChildren) { ArrayList<ResultFlag> flags = new ArrayList<ResultFlag>(); //标识为ResultFlag.CONSTRUCTOR flags.add(ResultFlag.CONSTRUCTOR); if ("idArg".equals(argChild.getName())) { //标识为一个ID属性 flags.add(ResultFlag.ID); } //创建一个ResultMapping对象,并加入resultMappings //这里说明了,constructor下有多个几子节点,就会产生多少个resultMapping对象 resultMappings.add(buildResultMappingFromContext(argChild, resultType, flags)); } }buildResultMappingFromContext()方法生成了一个ResultMapping对象,解析如下节点都会调用这个方法来实现:
- constructor/idArg
- constructor/arg
- result
- association
- collection
private ResultMapping buildResultMappingFromContext(XNode context, Class<?> resultType, ArrayList<ResultFlag> flags) throws Exception { 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"); //这里需要特殊说明,一个resultMapping可以对应一个resultMap对应,我们称之为内部映射 String nestedResultMap = context.getStringAttribute("resultMap", processNestedResultMappings(context, Collections.<ResultMapping> emptyList())); String notNullColumn = context.getStringAttribute("notNullColumn"); String columnPrefix = context.getStringAttribute("columnPrefix"); String typeHandler = context.getStringAttribute("typeHandler"); String resulSet = 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); //这里的ResultMapping生成的方法跟ResultMap生成的过程大同小异,这里不再做讨论 return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, nestedSelect, nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resulSet, foreignColumn, lazy); }
//生成一个内部的ReulstMapp对象,并加到Congruation中 private String processNestedResultMappings(XNode context, List<ResultMapping> resultMappings) throws Exception { //只有association, collection, case节点才会生成内部映射,其他不生成,返回null if ("association".equals(context.getName()) || "collection".equals(context.getName()) || "case".equals(context.getName())) { if (context.getStringAttribute("select") == null) { //这里类似一个递归调用,需要注意内部映射的ID是Mybatis自动生成的,不是在配置文件里读取的 ResultMap resultMap = resultMapElement(context, resultMappings); return resultMap.getId(); } } return null; }
再来看看内部映射的ID是怎么生成的:XMLMapperBuilder.resultMapElement方法
private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception { ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier()); String id = resultMapNode.getStringAttribute("id", resultMapNode.getValueBasedIdentifier()); //getValueBasedIdentifier这就是内部映射生成ID的地方,这个只是一个标识,保证唯一性即可!这里不再讨论! ... }
小结
在此,XMLMapperBuilder对配置文件中的resultMap元素的解析并生成ResultMap对象的分析基本完成。这里总结几点:
- ResultMap对象是结果集中的一行记录和一个java对象的对应关系。
- ResultMapping对象是结果集中的列与java对象的属性之间的对应关系。
- ResultMapp由id,type等基本的属性组成外,还包含多个ResultMapping对象。这类似于一个java对象由多个属性组成一个道理。
- ResultMapping最主要的属性column(结果集字段名),property(java对象的属性),ResultMapping可以指向一个内查询或内映射。
XMLMapperBuilder调用如下方法来解析并生成ResultMap对象
resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings)
- 在resultMapElement方法中调用
private ResultMapping buildResultMappingFromContext(XNode context, Class<?> resultType, ArrayList<ResultFlag> flags)
方法来子节点解析成ResultMapping对象。 - ResultMap和ResultMapping对象都是由相对应的Builder构建的。Builder只是进行了一些数据验证,并没有太多的业务逻辑。
0 0
- Mybatis3源码分析(04)-加载Configuration-XMLMapperBuilder加载ResultMap
- Mybatis3源码分析(03)-加载Configuration-ResultMap说明
- Mybatis3源码分析(02)-加载Configuration-XMLConfigBuilder
- Mybatis3源码分析(07)-加载Configuration-总结
- Mybatis3源码分析(05)-加载Configuration-加载MappedStatement
- Mybatis3源码分析(06)-加载Configuration-缓存配置加载
- Mybatis3源码分析(08)-加载Configuration-使用到的设计模式
- XMLMapperBuilder源码分析
- Mybatis3源码分析(20)-Mapper实现-配置加载
- Mybatis3源码分析(11)-Sql解析执行-BoundSql的加载-1
- Mybatis3源码分析(13)-Sql解析执行-BoundSql的加载-2
- Mybatis 源码解析(一) - Configuration.xml加载
- mybatis3--8.延迟加载
- mybatis3--延迟加载,缓存
- SpringMVC加载WebApplicationContext源码分析
- SpringMVC加载WebApplicationContext源码分析
- springmvc 加载WebApplicationContext源码分析
- Android资源加载源码分析
- html乱码原因与网页乱码解决方法
- 机器人路径规划_A*算法
- Windows Phone版使用移动广告平台集成Google Mobile Ads SDK教程
- Android佳博网络打印机例子
- 双向Dijstra算法
- Mybatis3源码分析(04)-加载Configuration-XMLMapperBuilder加载ResultMap
- JAVA NIO(二)基础 内存管理 文件锁定 Socket服务器客户端通信
- 互联网软件架构——RESTful架构
- 基于opencv的阈值分割
- android中利用LruCache对图片进行缓存处理
- 构建灵活的UI之Fragment(二)
- linux下who am i和whoami的区别
- Libvirt错误总结
- 维特比算法在隐马尔可夫模型中的应用