美文网首页
MyBatis源码阅读【加载】(二)加载映射文件流程

MyBatis源码阅读【加载】(二)加载映射文件流程

作者: 云芈山人 | 来源:发表于2021-05-16 23:59 被阅读0次

    一、相关类与对象说明

    • XMLMapperBuilder

      专门用来解析mapper映射文件
    public XMLMapperBuilder(Reader reader, Configuration configuration, String resource, Map<String, XNode> sqlFragments, String namespace);
    public XMLMapperBuilder(Reader reader, Configuration configuration, String resource, Map<String, XNode> sqlFragments) ;
    public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments, String namespace);
    public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments);
    
    
    • XPathParser

      用来使用XPath语法解析XML的解析器

    • MapperRegistry

      mapper的注册中心,用于注册mapper

    • MapperBuilderAssistant

      用于构建MappedStatement对象的

    • XMLStatementBuilder

      专门用来解析MappedStatement

    • MappedStatement对象

      MyBatis的mapper文件最终会被解析器,解析成MappedStatement,其中insert|update|delete|select每一个标签分别对应一个MappedStatement

      private String resource;
      private Configuration configuration;
      //节点中的id属性加要命名空间
      private String id;
      //尝试影响驱动程序每次批量返回的结果行数和这个设置值相等
      private Integer fetchSize;
      //SQL超时时间
      private Integer timeout;
      //Statement的类型,STATEMENT/PREPARE/CALLABLE
      private StatementType statementType;
      //结果集类型,FORWARD_ONLY/SCROLL_SENSITIVE/SCROLL_INSENSITIVE
      private ResultSetType resultSetType;
      //表示解析出来的SQL
      private SqlSource sqlSource;
      //缓存
      private Cache cache;
      //已废弃
      private ParameterMap parameterMap;
      //对应的ResultMap
      private List<ResultMap> resultMaps;
      private boolean flushCacheRequired;
      private boolean useCache;
      private boolean resultOrdered;
      //SQL类型,INSERT/SELECT/DELETE
      private SqlCommandType sqlCommandType;
      //和SELECTKEY标签有关
      private KeyGenerator keyGenerator;
      private String[] keyProperties;
      private String[] keyColumns;
      private boolean hasNestedResultMaps;
      //数据库ID,用来区分不同环境
      private String databaseId;
      private Log statementLog;
      private LanguageDriver lang;
      //多结果集时
      private String[] resultSets;
    
      MappedStatement() {
        // constructor disabled
      }
    

    二、XML映射文件

    MyBatis 的真正强大在于它的语句映射,减少使用成本,让用户能更专注于 SQL 代码。


    SQL 映射文件顶级元素.png
    • select

    select 元素允许你配置很多属性来配置每条语句的行为细节。

    <select
      id="selectPerson"
      parameterType="int"
      parameterMap="deprecated"
      resultType="hashmap"
      resultMap="personResultMap"
      flushCache="false"
      useCache="true"
      timeout="10"
      fetchSize="256"
      statementType="PREPARED"
      resultSetType="FORWARD_ONLY">
    
    select的属性.png
    • insert, update 和 delete

      数据变更语句 insert,update 和 delete 的实现非常接近
    <insert
      id="insertAuthor"
      parameterType="domain.blog.Author"
      flushCache="true"
      statementType="PREPARED"
      keyProperty=""
      keyColumn=""
      useGeneratedKeys=""
      timeout="20">
    
    <update
      id="updateAuthor"
      parameterType="domain.blog.Author"
      flushCache="true"
      statementType="PREPARED"
      timeout="20">
    
    <delete
      id="deleteAuthor"
      parameterType="domain.blog.Author"
      flushCache="true"
      statementType="PREPARED"
      timeout="20">
    
    Insert, Update, Delete 元素的属性.png
    • 动态sql

    借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类

    • if
    • choose (when, otherwise)
      MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句
    <select id="findActiveBlogLike"
         resultType="Blog">
      SELECT * FROM BLOG WHERE state = ‘ACTIVE’
      <choose>
        <when test="title != null">
          AND title like #{title}
        </when>
        <when test="author != null and author.name != null">
          AND author_name like #{author.name}
        </when>
        <otherwise>
          AND featured = 1
        </otherwise>
      </choose>
    </select>
    
    • trim (where, set)
    <trim prefix="WHERE" prefixOverrides="AND |OR ">
      ...
    </trim>
    
    <trim prefix="SET" suffixOverrides=",">
      ...
    </trim>
    
    
    • foreach
      动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候)。比如:
    <select id="selectPostIn" resultType="domain.blog.Post">
      SELECT *
      FROM POST P
      WHERE ID in
      <foreach item="item" index="index" collection="list"
          open="(" separator="," close=")">
            #{item}
      </foreach>
    </select>
    
    • script
      要在带注解的映射器接口类中使用动态 SQL,可以使用 script 元素。比如:
    @Update({"<script>",
          "update Author",
          "  <set>",
          "    <if test='username != null'>username=#{username},</if>",
          "    <if test='password != null'>password=#{password},</if>",
          "    <if test='email != null'>email=#{email},</if>",
          "    <if test='bio != null'>bio=#{bio}</if>",
          "  </set>",
          "where id=#{id}",
          "</script>"})
        void updateAuthorValues(Author author);
    
    • bind
      bind 元素允许你在 OGNL 表达式以外创建一个变量,并将其绑定到当前的上下文。比如:
    <select id="selectBlogsLike" resultType="Blog">
      <bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
      SELECT * FROM BLOG
      WHERE title LIKE #{pattern}
    </select>
    

    三、加载映射文件流程

    入口:XMLConfigBuilder#mapperElement

    解析全局配置文件中的<mappers>标签

    /**
       * 解析<mappers>标签
       * @param parent  mappers标签对应的XNode对象
       * @throws Exception
       */
      private void mapperElement(XNode parent) throws Exception {
        if (parent != null) {
          // 获取<mappers>标签的子标签
          for (XNode child : parent.getChildren()) {
            // <package>子标签
            if ("package".equals(child.getName())) {
              // 获取mapper接口和mapper映射文件对应的package包名
              String mapperPackage = child.getStringAttribute("name");
              // 将包下所有的mapper接口以及它的代理对象存储到一个Map集合中,key为mapper接口类型,value为代理对象工厂
              configuration.addMappers(mapperPackage);
            } else {// <mapper>子标签
              // 获取<mapper>子标签的resource属性
              String resource = child.getStringAttribute("resource");
              // 获取<mapper>子标签的url属性
              String url = child.getStringAttribute("url");
              // 获取<mapper>子标签的class属性
              String mapperClass = child.getStringAttribute("class");
              // 它们是互斥的
              if (resource != null && url == null && mapperClass == null) {
                ErrorContext.instance().resource(resource);
                InputStream inputStream = Resources.getResourceAsStream(resource);
                // 专门用来解析mapper映射文件
                XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
                // 通过XMLMapperBuilder解析mapper映射文件
                mapperParser.parse();
              } else if (resource == null && url != null && mapperClass == null) {
                ErrorContext.instance().resource(url);
                InputStream inputStream = Resources.getUrlAsStream(url);
                XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
                // 通过XMLMapperBuilder解析mapper映射文件
                mapperParser.parse();
              } else if (resource == null && url == null && mapperClass != null) {
                Class<?> mapperInterface = Resources.classForName(mapperClass);
                // 将指定mapper接口以及它的代理对象存储到一个Map集合中,key为mapper接口类型,value为代理对象工厂
                configuration.addMapper(mapperInterface);
              } else {
                throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
              }
            }
          }
        }
      }
    

    流程图

    加载映射文件流程.png

    加载映射文件流程

    一、<package>子标签

    • 流程图

    package子标签解析.png
    • 1.Configuration#addMappers

    将包下所有的mapper接口以及它的代理对象存储到一个Map集合中,key为mapper接口类型,value为代理对象工厂

    public void addMappers(String packageName) {
        mapperRegistry.addMappers(packageName);
    }
    
    • 1.1MapperRegistry#addMappers

    将Mapper接口添加到MapperRegistry中

    //1
    public void addMappers(String packageName) {
        addMappers(packageName, Object.class);
    }
    
    //2
    public void addMappers(String packageName, Class<?> superType) {
        ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
        // 根据package名称,加载该包下Mapper接口文件(不是映射文件)
        resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
        // 获取加载的Mapper接口
        Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
        for (Class<?> mapperClass : mapperSet) {
          // 将Mapper接口添加到MapperRegistry中
          addMapper(mapperClass);
        }
      }
    
    //3
    public <T> void addMapper(Class<T> type) {
        if (type.isInterface()) {
          // 如果Map集合中已经有该mapper接口的映射,就不需要再存储了
          if (hasMapper(type)) {
            throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
          }
          boolean loadCompleted = false;
          try {
            // 将mapper接口以及它的代理对象存储到一个Map集合中,key为mapper接口类型,value为代理对象工厂
            knownMappers.put(type, new MapperProxyFactory<T>(type));
            // It's important that the type is added before the parser is run
            // otherwise the binding may automatically be attempted by the
            // mapper parser. If the type is already known, it won't try.
            
            // 用来解析注解方式的mapper接口
            MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
            // 解析注解方式的mapper接口
            parser.parse();
            loadCompleted = true;
          } finally {
            if (!loadCompleted) {
              knownMappers.remove(type);
            }
          }
        }
      }
    
    • 1.1.1.MapperAnnotationBuilder#parse

    解析注解方式的mapper接口

    public void parse() {
        // 获取mapper接口的全路径
        String resource = type.toString();
        // 是否解析过该mapper接口
        if (!configuration.isResourceLoaded(resource)) {
          // 先解析mapper映射文件
          loadXmlResource();
          // 设置解析标识
          configuration.addLoadedResource(resource);
          // Mapper构建者助手
          assistant.setCurrentNamespace(type.getName());
          // 解析CacheNamespace注解
          parseCache();
          // 解析CacheNamespaceRef注解
          parseCacheRef();
          Method[] methods = type.getMethods();
          for (Method method : methods) {
            try {
              // issue #237
              if (!method.isBridge()) {
                // 每个mapper接口中的方法,都解析成MappedStatement对象
                parseStatement(method);
              }
            } catch (IncompleteElementException e) {
              configuration.addIncompleteMethod(new MethodResolver(this, method));
            }
          }
        }
        //去检查所有的incompleteMethods,如果可以解析了.那就移除
        parsePendingMethods();
      }
    
    • 1.1.1.1 MapperAnnotationBuilder#parseStatement

    每个mapper接口中的方法,都解析成MappedStatement对象

    void parseStatement(Method method) {
        // 获取Mapper接口的形参类型
        Class<?> parameterTypeClass = getParameterType(method);
        // 解析Lang注解
        LanguageDriver languageDriver = getLanguageDriver(method);
        // 
        SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);
        if (sqlSource != null) {
          Options options = method.getAnnotation(Options.class);
          // 组装mappedStatementId
          final String mappedStatementId = type.getName() + "." + method.getName();
          Integer fetchSize = null;
          Integer timeout = null;
          StatementType statementType = StatementType.PREPARED;
          ResultSetType resultSetType = null;
          // 获取该mapper接口中的方法是CRUD操作的哪一种
          SqlCommandType sqlCommandType = getSqlCommandType(method);
          // 是否是SELECT操作
          boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
          boolean flushCache = !isSelect;
          boolean useCache = isSelect;
    
          // 主键生成器,用于主键返回
          KeyGenerator keyGenerator;
          String keyProperty = null;
          String keyColumn = null;
          if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
            // first check for SelectKey annotation - that overrides everything else
            SelectKey selectKey = method.getAnnotation(SelectKey.class);
            if (selectKey != null) {
              keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver);
              keyProperty = selectKey.keyProperty();
            } else if (options == null) {
              keyGenerator = configuration.isUseGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
            } else {
              keyGenerator = options.useGeneratedKeys() ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
              keyProperty = options.keyProperty();
              keyColumn = options.keyColumn();
            }
          } else {
            keyGenerator = NoKeyGenerator.INSTANCE;
          }
    
          if (options != null) {
            if (FlushCachePolicy.TRUE.equals(options.flushCache())) {
              flushCache = true;
            } else if (FlushCachePolicy.FALSE.equals(options.flushCache())) {
              flushCache = false;
            }
            useCache = options.useCache();
            fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; //issue #348
            timeout = options.timeout() > -1 ? options.timeout() : null;
            statementType = options.statementType();
            resultSetType = options.resultSetType();
          }
    
          // 处理ResultMap注解
          String resultMapId = null;
          ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);
          if (resultMapAnnotation != null) {
            String[] resultMaps = resultMapAnnotation.value();
            StringBuilder sb = new StringBuilder();
            for (String resultMap : resultMaps) {
              if (sb.length() > 0) {
                sb.append(",");
              }
              sb.append(resultMap);
            }
            resultMapId = sb.toString();
          } else if (isSelect) {
            resultMapId = parseResultMap(method);
          }
    
          // 通过Mapper构建助手,创建一个MappedStatement对象,封装信息
          assistant.addMappedStatement(
              mappedStatementId,
              sqlSource,
              statementType,
              sqlCommandType,
              fetchSize,
              timeout,
              // ParameterMapID
              null,
              parameterTypeClass,
              resultMapId,
              getReturnType(method),
              resultSetType,
              flushCache,
              useCache,
              // TODO gcode issue #577
              false,
              keyGenerator,
              keyProperty,
              keyColumn,
              // DatabaseID
              null,
              languageDriver,
              // ResultSets
              options != null ? nullOrEmpty(options.resultSets()) : null);
        }
      }
    
    • 1.1.1.1.2 MapperBuilderAssistant#addMappedStatement

    通过Mapper构建助手,创建一个MappedStatement对象,封装信息

    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;
    
        //利用构建者模式,去创建MappedStatement.Builder,用于创建MappedStatement对象
        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.Builder,构建一个MappedStatement
        MappedStatement statement = statementBuilder.build();
        // 将MappedStatement对象存储到Configuration中的Map集合中,key为statement的id,value为MappedStatement对象
        configuration.addMappedStatement(statement);
        return statement;
      }
    

    二、<mapper>子标签

    • 流程图

    mapper子标签解析.png
    • 1.XMLMapperBuilder#构造函数

      专门用来解析mapper映射文件
    public XMLMapperBuilder(InputStream inputStream, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
        this(new XPathParser(inputStream, true, configuration.getVariables(), new XMLMapperEntityResolver()),
            configuration, resource, sqlFragments);
      }
    
    • 1.1XPathParser#构造函数

      用来使用XPath语法解析XML的解析器
    public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
        commonConstructor(validation, variables, entityResolver);
        // 解析XML文档为Document对象
        this.document = createDocument(new InputSource(inputStream));
      }
    
    • 1.1.1 XPathParser#createDocument

      创建Mapper映射文件对应的Document对象
    private Document createDocument(InputSource inputSource) {
        // important: this must only be called AFTER common constructor
        try {
          DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
          // 进行dtd或者Schema校验
          factory.setValidating(validation);
    
          factory.setNamespaceAware(false);
          // 设置忽略注释为true
          factory.setIgnoringComments(true);
          // 设置是否忽略元素内容中的空白
          factory.setIgnoringElementContentWhitespace(false);
          factory.setCoalescing(false);
          factory.setExpandEntityReferences(true);
    
          DocumentBuilder builder = factory.newDocumentBuilder();
          builder.setEntityResolver(entityResolver);
          builder.setErrorHandler(new ErrorHandler() {
            @Override
            public void error(SAXParseException exception) throws SAXException {
              throw exception;
            }
    
            @Override
            public void fatalError(SAXParseException exception) throws SAXException {
              throw exception;
            }
    
            @Override
            public void warning(SAXParseException exception) throws SAXException {
            }
          });
          // 通过dom解析,获取Document对象
          return builder.parse(inputSource);
        } catch (Exception e) {
          throw new BuilderException("Error creating document instance.  Cause: " + e, e);
        }
      }
    
    • 1.2 XMLMapperBuilder#构造函数

    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;
      }
    
    • 1.2.1MapperBuilderAssistant#构造函数

      用于构建MappedStatement对象的
    public MapperBuilderAssistant(Configuration configuration, String resource) {
        super(configuration);
        ErrorContext.instance().resource(resource);
        this.resource = resource;
      }
    
    • 2.XMLMapperBuilder#parse

    通过XMLMapperBuilder解析mapper映射文件

    public void parse() {
       // mapper映射文件是否已经加载过
       if (!configuration.isResourceLoaded(resource)) {
         // 从映射文件中的<mapper>根标签开始解析,直到完整的解析完毕
         configurationElement(parser.evalNode("/mapper"));
         // 标记已经解析
         configuration.addLoadedResource(resource);
         bindMapperForNamespace();
       }
    
       parsePendingResultMaps();
       parsePendingCacheRefs();
       parsePendingStatements();
     }
    
    • 2.1 XMLMapperBuilder#configurationElement

      从映射文件中的<mapper>根标签开始解析,直到完整的解析完毕
     /**
       * 解析映射文件
       * @param context 映射文件根节点<mapper>对应的XNode
       */
      private void configurationElement(XNode context) {
        try {
          // 获取<mapper>标签的namespace值,也就是命名空间
          String namespace = context.getStringAttribute("namespace");
          // 命名空间不能为空
          if (namespace == null || namespace.equals("")) {
            throw new BuilderException("Mapper's namespace cannot be empty");
          }
          
          // 设置当前的命名空间为namespace的值
          builderAssistant.setCurrentNamespace(namespace);
          // 解析<cache-ref>子标签
          cacheRefElement(context.evalNode("cache-ref"));
          // 解析<cache>子标签
          cacheElement(context.evalNode("cache"));
          
          // 解析<parameterMap>子标签
          parameterMapElement(context.evalNodes("/mapper/parameterMap"));
          // 解析<resultMap>子标签
          resultMapElements(context.evalNodes("/mapper/resultMap"));
          // 解析<sql>子标签,也就是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);
        }
      }
    
    • 2.1.1 XMLMapperBuilder#buildStatementFromContext

      用来创建MappedStatement对象的
    //1、构建MappedStatement
    private void buildStatementFromContext(List<XNode> list) {
        if (configuration.getDatabaseId() != null) {
          buildStatementFromContext(list, configuration.getDatabaseId());
        }
        // 构建MappedStatement
        buildStatementFromContext(list, null);
      }
    
    //2、专门用来解析MappedStatement
    private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
        for (XNode context : list) {
          // MappedStatement解析器
          final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
          try {
            // 解析select等4个标签,创建MappedStatement对象
            statementParser.parseStatementNode();
          } catch (IncompleteElementException e) {
            configuration.addIncompleteStatement(statementParser);
          }
        }
      }
    
    • 2.1.1.1 XMLStatementBuilder#构造函数

      专门用来解析MappedStatement
    public XMLStatementBuilder(Configuration configuration, MapperBuilderAssistant builderAssistant, XNode context, String databaseId) {
        super(configuration);
        this.builderAssistant = builderAssistant;
        this.context = context;
        this.requiredDatabaseId = databaseId;
      }
    
    • 2.1.1.2 XMLStatementBuilder#parseStatementNode

      解析<select><insert><update><delete>子标签
    /**
       * 解析<select>\<insert>\<update>\<delete>子标签
       */
      public void parseStatementNode() {
        // 获取statement的id属性(特别关键的值)
        String id = context.getStringAttribute("id");
        String databaseId = context.getStringAttribute("databaseId");
    
        if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
          return;
        }
    
        Integer fetchSize = context.getIntAttribute("fetchSize");
        Integer timeout = context.getIntAttribute("timeout");
        String parameterMap = context.getStringAttribute("parameterMap");
        // 获取入参类型
        String parameterType = context.getStringAttribute("parameterType");
        // 别名处理,获取入参对应的Java类型
        Class<?> parameterTypeClass = resolveClass(parameterType);
        // 获取ResultMap
        String resultMap = context.getStringAttribute("resultMap");
        // 获取结果映射类型
        String resultType = context.getStringAttribute("resultType");
        String lang = context.getStringAttribute("lang");
        LanguageDriver langDriver = getLanguageDriver(lang);
        
        // 别名处理,获取返回值对应的Java类型
        Class<?> resultTypeClass = resolveClass(resultType);
        String resultSetType = context.getStringAttribute("resultSetType");
        
        // 设置默认StatementType为Prepared,该参数指定了后面的JDBC处理时,采用哪种Statement
        StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
        ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
    
        String nodeName = context.getNode().getNodeName();
        // 解析SQL命令类型是什么?确定操作是CRUD中的哪一种
        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
        // <include>标签解析
        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,解析SQL,封装SQL语句(未参数绑定)和入参信息
        SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
       
        String resultSets = context.getStringAttribute("resultSets");
        String keyProperty = context.getStringAttribute("keyProperty");
        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))
              ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
        }
    
        // 通过构建者助手,创建MappedStatement对象
        builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
            fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
            resultSetTypeEnum, flushCache, useCache, resultOrdered, 
            keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
      }
    
    • 2.1.1.2.1 MapperBuilderAssistant#addMappedStatement

      通过构建者助手,创建MappedStatement对象
    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;
    
        //利用构建者模式,去创建MappedStatement.Builder,用于创建MappedStatement对象
        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.Builder,构建一个MappedStatement
        MappedStatement statement = statementBuilder.build();
        // 将MappedStatement对象存储到Configuration中的Map集合中,key为statement的id,value为MappedStatement对象
        configuration.addMappedStatement(statement);
        return statement;
      }
    
    • 2.1.1.2.1.1 MappedStatement.Builder#构造函数

      利用构建者模式,去创建MappedStatement.Builder,用于创建MappedStatement对象
    public Builder(Configuration configuration, String id, SqlSource sqlSource, SqlCommandType sqlCommandType) {
          mappedStatement.configuration = configuration;
          mappedStatement.id = id;
          mappedStatement.sqlSource = sqlSource;
          mappedStatement.statementType = StatementType.PREPARED;
          mappedStatement.resultSetType = ResultSetType.DEFAULT;
          mappedStatement.parameterMap = new ParameterMap.Builder(configuration, "defaultParameterMap", null, new ArrayList<>()).build();
          mappedStatement.resultMaps = new ArrayList<>();
          mappedStatement.sqlCommandType = sqlCommandType;
          mappedStatement.keyGenerator = configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType) ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
          String logId = id;
          if (configuration.getLogPrefix() != null) {
            logId = configuration.getLogPrefix() + id;
          }
          mappedStatement.statementLog = LogFactory.getLog(logId);
          mappedStatement.lang = configuration.getDefaultScriptingLanguageInstance();
        }
    
    • 2.1.1.2.1.2 MappedStatement#build

      通过MappedStatement.Builder,构建一个MappedStatement
    public MappedStatement build() {
          assert mappedStatement.configuration != null;
          assert mappedStatement.id != null;
          assert mappedStatement.sqlSource != null;
          assert mappedStatement.lang != null;
          mappedStatement.resultMaps = Collections.unmodifiableList(mappedStatement.resultMaps);
          return mappedStatement;
        }
    

    相关文章

      网友评论

          本文标题:MyBatis源码阅读【加载】(二)加载映射文件流程

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