MyBatis源碼筆記(五) -- 節點解析

MyBatis會把XML文件中的節點解析封裝成ResultMap,供結果映射使用

從XMLMapperBuilder類的resultMapElements方法看起

private void resultMapElements(List<XNode> list) throws Exception {
      //基本上就是循環把resultMap加入到Configuration裏去,保持2份,一份縮略,一分全名
    for (XNode resultMapNode : list) {
      try {
          //循環調resultMapElement
        resultMapElement(resultMapNode);
      } catch (IncompleteElementException e) {
        // ignore, it will be retried
      }
    }
}

private ResultMap resultMapElement(XNode resultMapNode) throws Exception {
   	return resultMapElement(resultMapNode, Collections.<ResultMapping> emptyList());
 	}


private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception {
//    <resultMap id="userResultMap" type="User">
//      <id property="id" column="user_id" />
//      <result property="username" column="username"/>
//      <result property="password" column="password"/>
//    </resultMap>
    ...
	//如果不存在id屬性則自動生成
    String id = resultMapNode.getStringAttribute("id",
        resultMapNode.getValueBasedIdentifier());
    //拿到返回類型
    String type = resultMapNode.getStringAttribute("type",
        resultMapNode.getStringAttribute("ofType",
            resultMapNode.getStringAttribute("resultType",
                resultMapNode.getStringAttribute("javaType"))));
    //高級功能,支持繼承
//  <resultMap id="carResult" type="Car" extends="vehicleResult">
//    <result property="doorCount" column="door_count" />
//  </resultMap>
    String extend = resultMapNode.getStringAttribute("extends");
    //autoMapping
    Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
	//獲得返回類型的Class
    Class<?> typeClass = resolveClass(type);
	//鑑別器
    Discriminator discriminator = null;
	//解析結果封裝成ResultMapping,放進list裏
    List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
    resultMappings.addAll(additionalResultMappings);
    List<XNode> resultChildren = resultMapNode.getChildren();
    for (XNode resultChild : resultChildren) {
      if ("constructor".equals(resultChild.getName())) {
        //1.解析result map的constructor
        processConstructorElement(resultChild, typeClass, resultMappings);
      } else if ("discriminator".equals(resultChild.getName())) {
        //2.解析result map的discriminator(鑑別器)
        discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
      } else {
		//3.解析除上面兩種以外的節點
        List<ResultFlag> flags = new ArrayList<ResultFlag>();
        if ("id".equals(resultChild.getName())) {
          flags.add(ResultFlag.ID);
        }
        //調buildResultMappingFromContext,得到ResultMapping
        resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
      }
    }
    //最後再調ResultMapResolver得到ResultMap
    ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
    try {
      return resultMapResolver.resolve();
    } catch (IncompleteElementException  e) {
      configuration.addIncompleteResultMap(resultMapResolver);
      throw e;
    }
}

上面解析分了三種情況:1.解析result map的constructor;2.解析result map的discriminator(鑑別器);3.解析除上面兩種以外的節點

1.解析result map的constructor

//<constructor>
//  <idArg column="blog_id" javaType="int"/>
//</constructor>
  private void processConstructorElement(XNode resultChild, Class<?> resultType, List<ResultMapping> resultMappings) throws Exception {
    List<XNode> argChildren = resultChild.getChildren();
    for (XNode argChild : argChildren) {
      List<ResultFlag> flags = new ArrayList<ResultFlag>();
      //結果標誌加上ID和CONSTRUCTOR
      flags.add(ResultFlag.CONSTRUCTOR);
      if ("idArg".equals(argChild.getName())) {
        flags.add(ResultFlag.ID);//加上ID標誌
      }
      resultMappings.add(buildResultMappingFromContext(argChild, resultType, flags));
    }
  }

private ResultMapping buildResultMappingFromContext(XNode context, Class<?> resultType, List<ResultFlag> flags) throws Exception {
	//<id property="id" column="author_id"/>
	//<result property="username" column="author_username"/>
	//下面拿到各種屬性
    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");
    //處理嵌套的result map
    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);
    @SuppressWarnings("unchecked")
    Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
    JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
    //去調builderAssistant.buildResultMapping
    return builderAssistant.buildResultMapping(resultType, property, column, javaTypeClass, jdbcTypeEnum, nestedSelect, nestedResultMap, notNullColumn, columnPrefix, typeHandlerClass, flags, resulSet, foreignColumn, lazy);
  }

	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) {
    Class<?> javaTypeClass = resolveResultJavaType(resultType, property, javaType);
    TypeHandler<?> typeHandlerInstance = resolveTypeHandler(javaTypeClass, typeHandler);
    //解析複合的列名,一般用不到,返回的是空
    List<ResultMapping> composites = parseCompositeColumnName(column);
    if (composites.size() > 0) {
      column = null;
    }
    //構建result map
    ResultMapping.Builder builder = new ResultMapping.Builder(configuration, property, column, javaTypeClass);
    builder.jdbcType(jdbcType);
    builder.nestedQueryId(applyCurrentNamespace(nestedSelect, true));
    builder.nestedResultMapId(applyCurrentNamespace(nestedResultMap, true));
    builder.resultSet(resultSet);
    builder.typeHandler(typeHandlerInstance);
    builder.flags(flags == null ? new ArrayList<ResultFlag>() : flags);
    builder.composites(composites);
    builder.notNullColumns(parseMultipleColumnNames(notNullColumn));
    builder.columnPrefix(columnPrefix);
    builder.foreignColumn(foreignColumn);
    builder.lazy(lazy);
    return builder.build();
  }

最終是調用了助手類實例builderAssistant的buildResultMapping構建一個ResultMapping返回出去,然後加入ResultMapping的集合裏

2.解析result map的discriminator(鑑別器)

//<discriminator javaType="int" column="draft">
//  <case value="1" resultMap="DraftPostMap"/>
//</discriminator>
private Discriminator processDiscriminatorElement(XNode context, Class<?> resultType, List<ResultMapping> resultMappings) throws Exception {
    String column = context.getStringAttribute("column");
    String javaType = context.getStringAttribute("javaType");
    String jdbcType = context.getStringAttribute("jdbcType");
    String typeHandler = context.getStringAttribute("typeHandler");
    Class<?> javaTypeClass = resolveClass(javaType);
    @SuppressWarnings("unchecked")
    Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
    JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
    Map<String, String> discriminatorMap = new HashMap<String, String>();
	//解析子節點
    for (XNode caseChild : context.getChildren()) {
      String value = caseChild.getStringAttribute("value");
		//拿到resultMap屬性,拿不到就進行遞歸resultMap解析
      String resultMap = caseChild.getStringAttribute("resultMap", processNestedResultMappings(caseChild, resultMappings));
      discriminatorMap.put(value, resultMap);//存進map
    }
    return builderAssistant.buildDiscriminator(resultType, column, javaTypeClass, jdbcTypeEnum, typeHandlerClass, discriminatorMap);
 }

public Discriminator buildDiscriminator(
      Class<?> resultType,
      String column,
      Class<?> javaType,
      JdbcType jdbcType,
      Class<? extends TypeHandler<?>> typeHandler,
      Map<String, String> discriminatorMap) {
    ResultMapping resultMapping = buildResultMapping(
        resultType,
        null,
        column,
        javaType,
        jdbcType,
        null,
        null,
        null,
        null,
        typeHandler,
        new ArrayList<ResultFlag>(),
        null,
        null,
        false);
    Map<String, String> namespaceDiscriminatorMap = new HashMap<String, String>();
    for (Map.Entry<String, String> e : discriminatorMap.entrySet()) {
      String resultMap = e.getValue();
		//加上命名空間前綴
      resultMap = applyCurrentNamespace(resultMap, true);
      namespaceDiscriminatorMap.put(e.getKey(), resultMap);
    }
    Discriminator.Builder discriminatorBuilder = new Discriminator.Builder(configuration, resultMapping, namespaceDiscriminatorMap);
    return discriminatorBuilder.build();
}

上面可以看到,最終還是會創建一個resultMapping,而鑑別的映射結果則是存進map裏,最後傳給鑑別器構造器構建一個鑑別器返回

3.解析除上面兩種以外的節點

	resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));

這裏直接調用了buildResultMappingFromContext方法,上面解析第一種情況時已經列出。

當把resultMap下所有子節點解析封裝成ResultMapping,並裝進集合後,就進行最後一步:構建ResultMap對象

//最後再調ResultMapResolver得到ResultMap
   ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
   try {
     return resultMapResolver.resolve();
   } catch (IncompleteElementException  e) {
     configuration.addIncompleteResultMap(resultMapResolver);
     throw e;
   }

public ResultMap resolve() {
     //解析又去調用MapperBuilderAssistant.addResultMap
   	return assistant.addResultMap(this.id, this.type, this.extend, this.discriminator, this.resultMappings, this.autoMapping);
 	}

//增加ResultMap
 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 resultMapBuilder = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping);
    if (extend != null) {
      if (!configuration.hasResultMap(extend)) {
		//這裏還沒解析引用到的其他ResultMap,拋異常退出,最後再構建
        throw new IncompleteElementException("Could not find a parent resultmap with id '" + extend + "'");
      }
      ResultMap resultMap = configuration.getResultMap(extend);
		//拿到繼承ResultMappings
      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)) {
			//把父ResultMapping的構造方法節點去掉
            extendedResultMappingsIter.remove();
          }
        }
      }
      resultMappings.addAll(extendedResultMappings);
    }
    resultMapBuilder.discriminator(discriminator);
    ResultMap resultMap = resultMapBuilder.build();
	//存到configuration中
    configuration.addResultMap(resultMap);
    return resultMap;
 }

最終解析Result配置信息封裝成ResultMap對象,並存進configuration中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章