XMLMapperBuilder解析*mapper.xml

springboot的MybatisAutoConfiguration自动配置类会创建SqlSessionFactory,创建过程就是填充configuration属性,调用buildSqlSessionFactory()方法完成SqlSessionFactory创建,这其中就会创建XMLMapperBuilder解析mapper.xml和XMLConfigBuilder解析mabatis-config.xml

关键方法parse
这个方法就是解析mapper中的所有标签,封装到configuration的属性中。然后构建mapper与xml的关系,后面查询时方便调用。

  public void parse() {
    if (!configuration.isResourceLoaded(resource)) {
      //处理<mapper >节点 也就是解析所有xml中的所有标签 例如select resultmap
      configurationElement(parser.evalNode("/mapper"));
      //将 resource 添加到 Configuration.loadedResources 集合 保存 它是 HashSet<String>
	  //类型的集合,其中记录了已经加载过的映射文件
      configuration.addLoadedResource(resource);
      //2.构建mapper与xml的关系 通过xml里定义的namespace
      bindMapperForNamespace();
    }
	//处理解析失败的<resultMap>标签
    parsePendingResultMaps();
    parsePendingCacheRefs();
    //处理解析失败的SQL语句
    parsePendingStatements();
  }

parse方法是被循环调用的,也就是每个mapper.xml都会执行一次。

关键方法:configurationElement
这里会解析xml中的每个标签和sql语句
重要的有resultMap、sql、select、delete、update、insert
在这里插入图片描述
关键方法buildStatementFromContext方法
这个方法会解析select delete等标签以及内部的where if等标签,如果有where这样的标签 则会封装成DynamicSqlSource,如果是普通的查询语句(select * from departments where department_id=#{depId})则会被解析成RawSqlSource,这个属性会被存在configuration的mappedstatements属性中,属性名称为sqlSource。
关键方法resultMapElements方法,解析resultMap标签。

1. 解析resultMap标签

解析完放到Configuration的resultMaps属性中,一种是全限定名的形式,一种是标签名的形式(兼容老版本?)。
在这里插入图片描述
XMLMapperBuilder类的resultMapElements方法
就是获取到resultMap的各种属性,然后调用ResultMapResolver 的resolve方法继续处理

  private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings, Class<?> enclosingType) {
    ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
    //拿到reusltMap标签上的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<>(additionalResultMappings);
    //拿到子标签 也就是resultMap中映射的所有字段
    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<>();
        //判断id标签
        if ("id".equals(resultChild.getName())) {
          flags.add(ResultFlag.ID);
        }
        //添加到resultMappings中
        resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
      }
    }
    //获取到resultMap的id属性的值
    String id = resultMapNode.getStringAttribute("id",
            resultMapNode.getValueBasedIdentifier());
    String extend = resultMapNode.getStringAttribute("extends");
    Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
    //构建一个ResultMapResolver 传入上面获取到的type id resultMappings
    ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
    try {
      //调用resolve方法,给configuration中的resultMaps属性赋值
      return resultMapResolver.resolve();
    } catch (IncompleteElementException e) {
      configuration.addIncompleteResultMap(resultMapResolver);
      throw e;
    }
  }

ResultMapResolver 的resolve方法
继续调用本类的MapperBuilderAssistant的addResultMap方法
在这里插入图片描述
MapperBuilderAssistant类的addResultMap方法
构建resultMap添加到Configuration中

在这里插入图片描述
这里用处就是在构建MappedStatement.Builder时从这个Confiuration中拿到resultMap,后面查询完解析结果时是从mappedStatement中拿到的resultMap。
解析完的resultMap
在这里插入图片描述

2. 解析select等标签

XMLMapperBuilder类的buildStatementFromContext方法
传入所有标签,遍历每个标签构建XMLStatementBuilder进行解析
在这里插入图片描述
XMLStatementBuilder的parseStatementNode方法

  public void parseStatementNode() {
    //获取标签的id
    String id = context.getStringAttribute("id");
    String databaseId = context.getStringAttribute("databaseId");

    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
      return;
    }
	//拿到nodeName 也就是select delete等
    String nodeName = context.getNode().getNodeName();
    //sql类型 也就是select delete等
    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 Fragments before parsing
    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);

    // Parse selectKey after includes and remove them.
    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 关键方法 解析sql的 处理#{} where标签等等
    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);
    //resultMap
    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");
	//调用MapperBuilderAssistant的addMappedStatement方法
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered,
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
  }

MapperBuilderAssistant类的addMappedStatement方法
构建MappedStatement ,添加到configuration的MappedStatements属性中

	//很多方法参数
  public MappedStatement addMappedStatement(
      String id,
      SqlSource sqlSource,
      StatementType statementType,
      SqlCommandType sqlCommandType,
      Integer fetchSize,
      Integer timeout,
      String parameterMap,
      Class<?> parameterType,
      String resultMap,
      Class<?> resultType,
      ResultSetType resultSetType,
      boolean flushCache,
      boolean useCache,
      boolean resultOrdered,
      KeyGenerator keyGenerator,
      String keyProperty,
      String keyColumn,
      String databaseId,
      LanguageDriver lang,
      String resultSets) {

    if (unresolvedCacheRef) {
      throw new IncompleteElementException("Cache-ref not yet resolved");
    }

    id = applyCurrentNamespace(id, false);
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
	//构建一个builder
    MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
        .resource(resource)
        .fetchSize(fetchSize)
        .timeout(timeout)
        .statementType(statementType)
        .keyGenerator(keyGenerator)
        .keyProperty(keyProperty)
        .keyColumn(keyColumn)
        .databaseId(databaseId)
        .lang(lang)
        .resultOrdered(resultOrdered)
        .resultSets(resultSets)
        .resultMaps(getStatementResultMaps(resultMap, resultType, id))
        .resultSetType(resultSetType)
        .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
        .useCache(valueOrDefault(useCache, isSelect))
        .cache(currentCache);

    ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
    if (statementParameterMap != null) {
      statementBuilder.parameterMap(statementParameterMap);
    }
	//构建MappedStatement 
    MappedStatement statement = statementBuilder.build();
    //保存到configuration的MappedStatements属性中
    configuration.addMappedStatement(statement);
    return statement;
  }

MappedStatement很关键很关键很关键,后面查询时都是从这里获取xml中的信息 包括sql resultMap等

StatementBuilder的build方法
build方法就很简单了,直接返回,因为在创建builder的时候,属性基本就填充完了。
在这里插入图片描述
MappedStatement的各种属性。
在这里插入图片描述

3. 将#{} 变成?(静态sql的处理)

变成?后会将里面的参数名称保存
XMLMapperBuilder类的buildStatementFromContext方法

在这里插入图片描述
继续本类的buildStatementFromContext
在这里插入图片描述
XMLStatementBuilder类的parseStatementNode方法
在这里插入图片描述
XMLLanguageDriver类的createSqlSource方法
在这里插入图片描述
XMLScriptBuilder类的parseScriptNode方法
在这里插入图片描述
RawSqlSource类的RawSqlSource方法
在这里插入图片描述
在这里插入图片描述
SqlSourceBuilder类的parse方法
在这里插入图片描述
GenericTokenParser类的parse方法
在这里插入图片描述
关键方法
在这里插入图片描述
就是handlertoken方法拼接的问号,并且保存了参数名称
在这里插入图片描述

4. 动态sql解析

解析xml时会调用XMLMapperBuilder的buildStatementFromContext方法,遍历所有select delete等标签,调用XMLStatementBuilder类的parseStatementNode方法。

在这里插入图片描述
XMLMapperBuilder的parseStatementNode方法
在这里插入图片描述
XMLLanguageDriver类的createSqlSource方法
在这里插入图片描述
XMLScriptBuilder类的parseScriptNode方法
调用parseDynamicTags方法完成动态sql的解析,如果有解析到动态标签,就把isDynamic置为true。然后构建动态sqlSource。否则构建RawSqlSource
在这里插入图片描述
XMLScriptBuilder类的parseDynamicTags方法

  protected MixedSqlNode parseDynamicTags(XNode node) {
    List<SqlNode> contents = new ArrayList<>();
    //拿到当前select标签下所有的子标签
    NodeList children = node.getNode().getChildNodes();
    //遍历
    for (int i = 0; i < children.getLength(); i++) {
      XNode child = node.newXNode(children.item(i));
      //普通的语句会走这里 ${}应该也是走这里 不常用 这里没做测试
      if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
        String data = child.getStringBody("");
        TextSqlNode textSqlNode = new TextSqlNode(data);
        if (textSqlNode.isDynamic()) {
          contents.add(textSqlNode);
          isDynamic = true;
        } else {
          contents.add(new StaticTextSqlNode(data));
        }
        //if where foreach等标签会走这里
      } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628
        //拿到标签的名字
        String nodeName = child.getNode().getNodeName();
        //根据标签名字 拿到对应的handler
        NodeHandler handler = nodeHandlerMap.get(nodeName);
        if (handler == null) {
          throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement.");
        }
        //调用handler处理,将结果保存到contents中
        handler.handleNode(child, contents);
        isDynamic = true;
      }
    }
    return new MixedSqlNode(contents);
  }

内容都保存在contents中,最后变成MixedSqlNode。
在这里插入图片描述
nodeHandlerMap 共有9中 处理动态标签。
在这里插入图片描述
XMLScriptBuilder类的内部类的IfHandler类的handleNode方法
继续调用parseDynamicTags解析子标签,也就是if标签里边具体的sql语句,然后获取test标签中的判断内容,然后封装成一个IfSqlNode类型,添加到contents中,然后返回。
在这里插入图片描述

最终将所有标签都处理完,返回一个MixedSqlNode用来构建DynamicSqlSource,然后再将DynamicSqlSource添加到mapperedStatement中。后来查询时获取sql时会用到。
最终的sqlsource
在这里插入图片描述
后面查询时就是获取这个sqlSource,然后拼接方法传参,得到最终的sql。

5. configuration关键属性

1.mapperRegistry

  • bindMapperForNamespace()方法实现的,对接口的全限定名和代理类进行绑定。实例化mapper时会来这里获取到每个mapper对应的MapperProxyFactory,这个工厂类会创建对应的MapperProxy代理类,用来代理mapper的方法,也就相当于对mapper进行了实例化。
    在这里插入图片描述

2.resultMaps
就是xml中每个方法定义的resultMap或实体类型的resultType
在这里插入图片描述
3.sqlFragments
每个xml中定义的sql标签
在这里插入图片描述
在这里插入图片描述
4.loadedResources

  • configuration.addLoadedResource(resource)方法实现的
    在这里插入图片描述
    这里有接口,接口的全限定名,对应的xml。
    5.mappedStatements(关键)
    在这里插入图片描述
    这里对应每个< select >、< insert >、< update >、< delete >标签,sqlSource是标签中写的sql,以及对应的resultMap。
  • 后期查询时就是通过这个key找到对应的mappedStatement,拿到sqlSource解析成最终的sql,然后进行查询,查询后拿到resultMaps进行参数映射。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值