Mybatis学习(六)--- Xml的解析

    在实际mybatis的应用中,我们写好mapper接口之后,需要提供对应的xml文件,这次我们主要分析是如何解析xml文件的。

XmlMappedBuilder

    解析的主要函数是:

//是否加载过该映射文件
public void parse() {
  if (!configuration.isResourceLoaded(resource)) {
    //处理mapper节点
    configurationElement(parser.evalNode("/mapper"));
    configuration.addLoadedResource(resource);
    //注册mapper接口
    bindMapperForNamespace();
  }
  //处理configurationElement()中解析失败的节点
  parsePendingResultMaps();
  parsePendingCacheRefs();
  parsePendingStatements();
}

    主要看下configurationElement()方法:

private void configurationElement(XNode context) {
  try {
    //获取namespace属性
    String namespace = context.getStringAttribute("namespace");
    if (namespace == null || namespace.equals("")) {
      throw new BuilderException("Mapper's namespace cannot be empty");
    }
    builderAssistant.setCurrentNamespace(namespace);
    //解析各个节点
    cacheRefElement(context.evalNode("cache-ref"));
    cacheElement(context.evalNode("cache"));
    parameterMapElement(context.evalNodes("/mapper/parameterMap"));
    //解析resultMap节点
    resultMapElements(context.evalNodes("/mapper/resultMap"));
    //解析sql节点
    sqlElement(context.evalNodes("/mapper/sql"));
    //解析select、insert、update、delete节点
    buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
  } catch (Exception e) {
    throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
  }
}

解析<resultMap>节点

    每个<resultmap>节点会解析成一个ResultMap对象,其中每个ResultMapping对象记录了结果集中一列与javaBean中一个属性的映射关系。在XmlMapperBuilder中的resultMapElement处理每一个节点。

private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings, Class<?> enclosingType) throws Exception {
  ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
  //获取type属性
  String type = resultMapNode.getStringAttribute("type",
      resultMapNode.getStringAttribute("ofType",
          resultMapNode.getStringAttribute("resultType",
              resultMapNode.getStringAttribute("javaType"))));
  Class<?> typeClass = resolveClass(type);
  if (typeClass == null) {
    typeClass = inheritEnclosingType(resultMapNode, enclosingType);
  }
  Discriminator discriminator = null;
  //解析的结果
  List<ResultMapping> resultMappings = new ArrayList<>();
  resultMappings.addAll(additionalResultMappings);
  //处理resultMap的子节点
  List<XNode> resultChildren = resultMapNode.getChildren();
  for (XNode resultChild : resultChildren) {
   //处理construct节点
    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<>();
      if ("id".equals(resultChild.getName())) {
        flags.add(ResultFlag.ID);
      }
      //通过buildResultMappingFromContext创建resultMapping节点
      resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
    }
  }
  String id = resultMapNode.getStringAttribute("id",
          resultMapNode.getValueBasedIdentifier());
  String extend = resultMapNode.getStringAttribute("extends");
  Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
  ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
  try {
    //得到resultMapping创建resultMap对象,并将对象添加到Configuration的resultMaps集合中
    return resultMapResolver.resolve();
  } catch (IncompleteElementException  e) {
    configuration.addIncompleteResultMap(resultMapResolver);
    throw e;
  }
}

解析sql节点

    

private void sqlElement(List<XNode> list, String requiredDatabaseId) {
  for (XNode context : list) { //遍历sql节点
    String databaseId = context.getStringAttribute("databaseId");
    String id = context.getStringAttribute("id");
    id = builderAssistant.applyCurrentNamespace(id, false);
    //检测sql的databaseid是否与configuration记录的databaseId一致
    if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) {
      //记录到集合中保存
      sqlFragments.put(id, context);
    }
  }
}

    配置文件中<sql>会解析成SqlSource,而 SQL 语句中定义的动态 SQL 节点,如 <where>,<if> 之类的使用 SqlNode 相关的实现类来表示。DynamicContext 该类主要用来存放解析动态 SQL 语句产生的 SQL 语句片段。

public interface SqlSource {
  //返回值 BoundSql 对象,它包含了 "?" 占位符的 SQL 语句,以及绑定的实参
  BoundSql getBoundSql(Object parameterObject);

}

    实现sqlSource接口的类有:

  • DynamicSqlSource 负责处理动态 SQL 语句,DynamicSqlSource 中的 SQL 还需要进一步解析才能被数据库执行
  • RawSqlSource 负责处理静态 SQL 语句
  • StaticSqlSource;处理后的 SQL ,StaticSqlSource 包含的 SQL 可能含有 “?” 占位符,可以被数据库直接执行
  • providerSqlSource

    当当SQL节点经过各个SqlNode解析后,SQL语句会被传到 SqlSourceBuilder 进一步解析。SqlSourceBuilder 主要完成两部:一是解析 #{} 占位符中的属性,二是把SQL中的 #{} 替换为 “?”

XmlStatementBuilder

    解析sql语句的节点,使用SqlSource接口表示映射文件中的sql语句。

public void parseStatementNode() {
  //id 属性和数据库标识
  String id = context.getStringAttribute("id");
  String databaseId = context.getStringAttribute("databaseId");

  if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
    return;
  }
  //获取节点的名称
  String nodeName = context.getNode().getNodeName();
  SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
  boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
  //获取节点的属性和对应属性的类型,
  boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
  boolean useCache = context.getBooleanAttribute("useCache", isSelect);
  boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

  // include节点的解析
  XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
  includeParser.applyIncludes(context.getNode());

  String parameterType = context.getStringAttribute("parameterType");
  //从注册的类型里面查找参数类型
  Class<?> parameterTypeClass = resolveClass(parameterType);

  String lang = context.getStringAttribute("lang");
  LanguageDriver langDriver = getLanguageDriver(lang);

  // 解析selectKey节点
  processSelectKeyNodes(id, parameterTypeClass, langDriver);

  // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
  KeyGenerator keyGenerator;
  String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
  keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
  if (configuration.hasKeyGenerator(keyStatementId)) {
    keyGenerator = configuration.getKeyGenerator(keyStatementId);
  } else {
    keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
        configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
        ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
  }

  SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
  StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
  Integer fetchSize = context.getIntAttribute("fetchSize");
  Integer timeout = context.getIntAttribute("timeout");
  String parameterMap = context.getStringAttribute("parameterMap");
  String resultType = context.getStringAttribute("resultType");
  Class<?> resultTypeClass = resolveClass(resultType);
  String resultMap = context.getStringAttribute("resultMap");
  String resultSetType = context.getStringAttribute("resultSetType");
  ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
  if (resultSetTypeEnum == null) {
    resultSetTypeEnum = configuration.getDefaultResultSetType();
  }
  //解析属性
  String keyProperty = context.getStringAttribute("keyProperty");
  String keyColumn = context.getStringAttribute("keyColumn");
  String resultSets = context.getStringAttribute("resultSets");
  //创建 MapperedStatement 对象,添加到 configuration 中,MapperedStatement对象的id是生成cache的一个属性 
  builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
      fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
      resultSetTypeEnum, flushCache, useCache, resultOrdered,
      keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值