mybatis mapper解析(4)

weixin_34194087發表於2018-07-18

開篇

 這篇文章的目的主要是想講清楚mapper標籤的解析過程,核心目的是想講清楚/mapper/resultMap/mapper/sqlselect|insert|update|delete 這三大類標籤的解析過程,以及生成最終的解析物件並儲存到Configuration當中。


解析過程時序圖

6302559-5ea4b7fac2ccc57d.png
mapper標籤解析過程


解析過程原始碼分析

mapper.xml檔案的常用格式

 mapper.xml當中通用的幾大類標籤,分別是resultMap,sql,以及select相關語句,我們的解析就是針對這幾類標籤進行解析,當然千萬千萬不要遺漏了namespace這個標籤。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.qbd.mapper.StudentMappers">
    <select id="findbyid" parameterType="Integer" resultMap="StudentResult">
        select *from student where id=#{id}
    </select>
    
    <select id="findbygradeid" parameterType="Integer" resultMap="StudentResult">
        select *from student where gid=#{gid}
    </select>
    
    <resultMap type="Student" id="StudentResult">
        <id property="id" column="id"/>
        <result property="name" column="name"/>
        <result property="age" column="age"/>
        <association property="address" column="addid" select="com.qbd.mapper.AddressMappers.findbyid">    
        </association>
        <association property="grade" column="gid" select="com.qbd.mapper.GradeMappers.findbyid">    
        </association>
    </resultMap>
</mapper>


mapper原始碼解析入口

 僅關注mapperElement(root.evalNode("mappers")),因為mappers裡面包含了真正執行的SQL語句的定義。

<mappers>
        <mapper resource="map/query.xml" />
        <mapper resource="map/insert.xml" />
        <mapper resource="map/update.xml" />
        <mapper resource="map/delete.xml" />
</mappers>

---------------------XMLConfigBuilder.java--------------------------------------
 //解析配置
  private void parseConfiguration(XNode root) {
    try {
      //分步驟解析
      //issue #117 read properties first
      //1.properties
      propertiesElement(root.evalNode("properties"));
      //2.型別別名
      typeAliasesElement(root.evalNode("typeAliases"));
      //3.外掛
      pluginElement(root.evalNode("plugins"));
      //4.物件工廠
      objectFactoryElement(root.evalNode("objectFactory"));
      //5.物件包裝工廠
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      //6.設定
      settingsElement(root.evalNode("settings"));
      // read it after objectFactory and objectWrapperFactory issue #631
      //7.環境
      environmentsElement(root.evalNode("environments"));
      //8.databaseIdProvider
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      //9.型別處理器
      typeHandlerElement(root.evalNode("typeHandlers"));
      //10.對映器
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }

 開始遍歷mappers標籤下的所有mapper逐一進行解析,一般我們通過resource標籤引入SQL定義的xml檔案,進而通過XMLMapperBuilder物件進行parse操作。

    //10.對映器
//  10.1使用類路徑
//  <mappers>
//    <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
//    <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
//    <mapper resource="org/mybatis/builder/PostMapper.xml"/>
//  </mappers>
//
//  10.2使用絕對url路徑
//  <mappers>
//    <mapper url="file:///var/mappers/AuthorMapper.xml"/>
//    <mapper url="file:///var/mappers/BlogMapper.xml"/>
//    <mapper url="file:///var/mappers/PostMapper.xml"/>
//  </mappers>
//
//  10.3使用java類名
//  <mappers>
//    <mapper class="org.mybatis.builder.AuthorMapper"/>
//    <mapper class="org.mybatis.builder.BlogMapper"/>
//    <mapper class="org.mybatis.builder.PostMapper"/>
//  </mappers>
//
//  10.4自動掃描包下所有對映器
//  <mappers>
//    <package name="org.mybatis.builder"/>
//  </mappers>
  private void mapperElement(XNode parent) throws Exception {
    if (parent != null) {
      for (XNode child : parent.getChildren()) {
        if ("package".equals(child.getName())) {
          //10.4自動掃描包下所有對映器
          String mapperPackage = child.getStringAttribute("name");
          configuration.addMappers(mapperPackage);
        } else {
          String resource = child.getStringAttribute("resource");
          String url = child.getStringAttribute("url");
          String mapperClass = child.getStringAttribute("class");
          if (resource != null && url == null && mapperClass == null) {
            //10.1使用類路徑
            ErrorContext.instance().resource(resource);
            InputStream inputStream = Resources.getResourceAsStream(resource);
            //對映器比較複雜,呼叫XMLMapperBuilder
            //注意在for迴圈裡每個mapper都重新new一個XMLMapperBuilder,來解析
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, 
                                                                 configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url != null && mapperClass == null) {
            //10.2使用絕對url路徑
            ErrorContext.instance().resource(url);
            InputStream inputStream = Resources.getUrlAsStream(url);
            //對映器比較複雜,呼叫XMLMapperBuilder
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, 
                                                                 url, configuration.getSqlFragments());
            mapperParser.parse();
          } else if (resource == null && url == null && mapperClass != null) {
            //10.3使用java類名
            Class<?> mapperInterface = Resources.classForName(mapperClass);
            //直接把這個對映加入配置
            configuration.addMapper(mapperInterface);
          } else {
            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
          }
        }
      }
    }
  }

----------------------XMLMapperBuilder.java---------------------
public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, 
                        Map<String, XNode> sqlFragments) {
    this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()),
        configuration, resource, sqlFragments);
  }

  private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
    super(configuration);
    this.builderAssistant = new MapperBuilderAssistant(configuration, resource);
    this.parser = parser;
    this.sqlFragments = sqlFragments;
    this.resource = resource;
  }

  //解析
  public void parse() {
    //如果沒有載入過再載入,防止重複載入
    if (!configuration.isResourceLoaded(resource)) {
      //配置mapper
      configurationElement(parser.evalNode("/mapper"));
      //標記一下,已經載入過了
      configuration.addLoadedResource(resource);
      //繫結對映器到namespace
      bindMapperForNamespace();
    }

    //還有沒解析完的東東這裡接著解析?  
    parsePendingResultMaps();
    parsePendingChacheRefs();
    parsePendingStatements();
  }


解析SQL定義的xml檔案

 核心標籤在於我們解析 /mapper/resultMap標籤,/mapper/sql標籤,select|insert|update|delete標籤。

-------------------XMLMapperBuilder.java-------------------
private void configurationElement(XNode context) {
    try {
      //1.配置namespace
      String namespace = context.getStringAttribute("namespace");
      if (namespace.equals("")) {
        throw new BuilderException("Mapper's namespace cannot be empty");
      }
      builderAssistant.setCurrentNamespace(namespace);
      //2.配置cache-ref
      cacheRefElement(context.evalNode("cache-ref"));
      //3.配置cache
      cacheElement(context.evalNode("cache"));
      //4.配置parameterMap(已經廢棄,老式風格的引數對映)
      parameterMapElement(context.evalNodes("/mapper/parameterMap"));
      //5.配置resultMap(高階功能)
      resultMapElements(context.evalNodes("/mapper/resultMap"));
      //6.配置sql(定義可重用的 SQL 程式碼段)
      sqlElement(context.evalNodes("/mapper/sql"));
      //7.配置select|insert|update|delete TODO
      buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
    }
  }


/mapper/resultMap解析

 解析resultMap放置到Configuration當中,這裡把所有resultMap的標籤列表開始進行解析,configuration.addResultMap(resultMap)。

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());
  }

  //5.1 配置resultMap
  private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception {
    //錯誤上下文
    //取得標示符   ("resultMap[userResultMap]")
    //    <resultMap id="userResultMap" type="User">
    //      <id property="id" column="user_id" />
    //      <result property="username" column="username"/>
    //      <result property="password" column="password"/>
    //    </resultMap>
    ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
    String id = resultMapNode.getStringAttribute("id",
        resultMapNode.getValueBasedIdentifier());
    //一般拿type就可以了,後面3個難道是相容老的程式碼?
    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<?> typeClass = resolveClass(type);
    Discriminator discriminator = null;
    List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
    resultMappings.addAll(additionalResultMappings);
    List<XNode> resultChildren = resultMapNode.getChildren();
    //解析這個xml檔案當中所有的<resultMap></resultMap> 節點
    for (XNode resultChild : resultChildren) {
      if ("constructor".equals(resultChild.getName())) {
        //解析result map的constructor
        processConstructorElement(resultChild, typeClass, resultMappings);
      } else if ("discriminator".equals(resultChild.getName())) {
        //解析result map的discriminator
        discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
      } else {
        List<ResultFlag> flags = new ArrayList<ResultFlag>();
        if ("id".equals(resultChild.getName())) {
          flags.add(ResultFlag.ID);
        }
        //調5.1.1 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;
    }
  }


    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 ResultMap resolve() {
      //解析又去呼叫MapperBuilderAssistant.addResultMap
    return assistant.addResultMap(this.id, this.type, this.extend, this.discriminator, this.resultMappings, this.autoMapping);
  }

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)) {
        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 = resultMapBuilder.build();
    configuration.addResultMap(resultMap);
    return resultMap;
  }


/mapper/sql片段解析

 遍歷所有的sql標籤挨個進行解析,然後放置到sqlFragments的map當中,sqlFragments.put(id, context)。

  //6 配置sql(定義可重用的 SQL 程式碼段)
  private void sqlElement(List<XNode> list) throws Exception {
    if (configuration.getDatabaseId() != null) {
      sqlElement(list, configuration.getDatabaseId());
    }
    sqlElement(list, null);
  }


  //6.1 配置sql
  //<sql id="userColumns"> id,username,password </sql>
  private void sqlElement(List<XNode> list, String requiredDatabaseId) throws Exception {
    for (XNode context : list) {
      String databaseId = context.getStringAttribute("databaseId");
      String id = context.getStringAttribute("id");
      id = builderAssistant.applyCurrentNamespace(id, false);
      //比較簡單,就是將sql片段放入hashmap,不過此時還沒有解析sql片段
      if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) {
        sqlFragments.put(id, context);
      }
    }
  }


select|insert|update|delete語句解析

 遍歷所有的select|insert|update|delete的標籤,挨個進行解析並儲存到configuration當中,configuration.addMappedStatement(statement)。後面使用的時候通過select等標籤的id直接獲取statement從而實現了interface和xml定義SQL語句的對映關係了。
 核心的SQL替換在方法includeParser.applyIncludes(context.getNode())當中實現。
 SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);這裡的SqlSource有多個實現細節。

---------------------XMLMapperBuilder.java------------------------

  //7.配置select|insert|update|delete
  private void buildStatementFromContext(List<XNode> list) {
    //呼叫7.1構建語句
    if (configuration.getDatabaseId() != null) {
      buildStatementFromContext(list, configuration.getDatabaseId());
    }
    buildStatementFromContext(list, null);
  }

  //7.1構建語句
  private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    for (XNode context : list) {
      //構建所有語句,一個mapper下可以有很多select
      //語句比較複雜,核心都在這裡面,所以呼叫XMLStatementBuilder
      final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, 
                                    context, requiredDatabaseId);
      try {
          //核心XMLStatementBuilder.parseStatementNode
        statementParser.parseStatementNode();
      } catch (IncompleteElementException e) {
          //如果出現SQL語句不完整,把它記下來,塞到configuration去
        configuration.addIncompleteStatement(statementParser);
      }
    }
  }


------------------------XMLStatementBuilder.java-------------------------

public void parseStatementNode() {
    String id = context.getStringAttribute("id");
    String databaseId = context.getStringAttribute("databaseId");

    //如果databaseId不匹配,退出
    if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
      return;
    }

    //暗示驅動程式每次批量返回的結果行數
    Integer fetchSize = context.getIntAttribute("fetchSize");
    //超時時間
    Integer timeout = context.getIntAttribute("timeout");
    //引用外部 parameterMap,已廢棄
    String parameterMap = context.getStringAttribute("parameterMap");
    //引數型別
    String parameterType = context.getStringAttribute("parameterType");
    Class<?> parameterTypeClass = resolveClass(parameterType);
    //引用外部的 resultMap(高階功能)
    String resultMap = context.getStringAttribute("resultMap");
    //結果型別
    String resultType = context.getStringAttribute("resultType");
    //指令碼語言,mybatis3.2的新功能
    String lang = context.getStringAttribute("lang");
    //得到語言驅動
    LanguageDriver langDriver = getLanguageDriver(lang);

    Class<?> resultTypeClass = resolveClass(resultType);
    //結果集型別,FORWARD_ONLY|SCROLL_SENSITIVE|SCROLL_INSENSITIVE 中的一種
    String resultSetType = context.getStringAttribute("resultSetType");
    //語句型別, STATEMENT|PREPARED|CALLABLE 的一種
    StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", 
                                                        StatementType.PREPARED.toString()));
    ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);

    //獲取命令型別(select|insert|update|delete)
    String nodeName = context.getNode().getNodeName();
    SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
    boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
    //是否要快取select結果
    boolean useCache = context.getBooleanAttribute("useCache", isSelect);
    //僅針對巢狀結果 select 語句適用:如果為 true,就是假設包含了巢狀結果集或是分組了,
    //這樣的話當返回一個主結果行的時候,就不會發生有對前面結果集的引用的情況。
    //這就使得在獲取巢狀的結果集的時候不至於導致記憶體不夠用。預設值:false。 
    boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);

    // Include Fragments before parsing
    //解析之前先解析<include>SQL片段
    XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
    includeParser.applyIncludes(context.getNode());

    // Parse selectKey after includes and remove them.
    //解析之前先解析<selectKey>
    processSelectKeyNodes(id, parameterTypeClass, langDriver);
    
    // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
    //解析成SqlSource,一般是DynamicSqlSource
    SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
    String resultSets = context.getStringAttribute("resultSets");
    //(僅對 insert 有用) 標記一個屬性, MyBatis 會通過 getGeneratedKeys 或者通過 insert 語句的 selectKey 子元素設定它的值
    String keyProperty = context.getStringAttribute("keyProperty");
    //(僅對 insert 有用) 標記一個屬性, MyBatis 會通過 getGeneratedKeys 或者通過 insert 語句的 selectKey 子元素設定它的值
    String keyColumn = context.getStringAttribute("keyColumn");
    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))
          ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
    }

    //又去調助手類
    builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
        fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
        resultSetTypeEnum, flushCache, useCache, resultOrdered, 
        keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
  }


-------------------------XMLLanguageDriver.java---------------------
  public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
    //用XML指令碼構建器解析
    XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
    return builder.parseScriptNode();
  }

  public SqlSource parseScriptNode() {
    List<SqlNode> contents = parseDynamicTags(context);
    MixedSqlNode rootSqlNode = new MixedSqlNode(contents);
    SqlSource sqlSource = null;
    if (isDynamic) {
      sqlSource = new DynamicSqlSource(configuration, rootSqlNode);
    } else {
      sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType);
    }
    return sqlSource;
  }


---------------------XMLIncludeTransformer.java-----------------------------------

//<select id="selectUsers" resultType="map">
//  select <include refid="userColumns"/>
//  from some_table
//  where id = #{id}
//</select>
  public void applyIncludes(Node source) {
    if (source.getNodeName().equals("include")) {
      //走到這裡,單獨解析<include refid="userColumns"/>
      //拿到SQL片段
      Node toInclude = findSqlFragment(getStringAttribute(source, "refid"));
      //遞迴呼叫自己,應用上?
      applyIncludes(toInclude);
      //總之下面就是將字串拼接進來,看不懂。。。
      if (toInclude.getOwnerDocument() != source.getOwnerDocument()) {
        toInclude = source.getOwnerDocument().importNode(toInclude, true);
      }
      source.getParentNode().replaceChild(toInclude, source);
      while (toInclude.hasChildNodes()) {
        toInclude.getParentNode().insertBefore(toInclude.getFirstChild(), toInclude);
      }
      toInclude.getParentNode().removeChild(toInclude);
    } else if (source.getNodeType() == Node.ELEMENT_NODE) {
        //一開始會走這段,取得所有兒子
      NodeList children = source.getChildNodes();
      for (int i=0; i<children.getLength(); i++) {
          //遞迴呼叫自己
        applyIncludes(children.item(i));
      }
    }
  }

------------------------------------MapperBuilderAssistant.java-----------------------------
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加上namespace字首
    id = applyCurrentNamespace(id, false);
    //是否是select語句
    boolean isSelect = sqlCommandType == SqlCommandType.SELECT;

    //又是建造者模式
    MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType);
    statementBuilder.resource(resource);
    statementBuilder.fetchSize(fetchSize);
    statementBuilder.statementType(statementType);
    statementBuilder.keyGenerator(keyGenerator);
    statementBuilder.keyProperty(keyProperty);
    statementBuilder.keyColumn(keyColumn);
    statementBuilder.databaseId(databaseId);
    statementBuilder.lang(lang);
    statementBuilder.resultOrdered(resultOrdered);
    statementBuilder.resulSets(resultSets);
    setStatementTimeout(timeout, statementBuilder);

    //1.引數對映
    setStatementParameterMap(parameterMap, parameterType, statementBuilder);
    //2.結果對映
    setStatementResultMap(resultMap, resultType, resultSetType, statementBuilder);
    setStatementCache(isSelect, flushCache, useCache, currentCache, statementBuilder);

    MappedStatement statement = statementBuilder.build();
    //建造好呼叫configuration.addMappedStatement
    configuration.addMappedStatement(statement);
    return statement;
  }


SqlSource的類圖

6302559-a72338a8d1b7d0c9.png
SqlSource.png

相關文章