美文网首页mybatis
mybatis mapper解析(4)

mybatis mapper解析(4)

作者: 晴天哥_王志 | 来源:发表于2018-07-18 23:33 被阅读71次

    开篇

     这篇文章的目的主要是想讲清楚mapper标签的解析过程,核心目的是想讲清楚/mapper/resultMap/mapper/sqlselect|insert|update|delete 这三大类标签的解析过程,以及生成最终的解析对象并保存到Configuration当中。

    解析过程时序图

    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的类图

    SqlSource.png

    相关文章

      网友评论

        本文标题:mybatis mapper解析(4)

        本文链接:https://www.haomeiwen.com/subject/knuppftx.html