解析<sql>节点

在映射配置文件中,可以使用<sql>节点定义可重用的~语句片段。当需要重用<sql>节点中定义的SQL语句片段时,只需要使用include节点引用相应的片段即可,这样,在编写SQL语句以及维护这些SQL语句时,都会比较方便。<include>节点的解析在后面详细介绍。

XMLMapperBuilder.sqlElement()方法负责解析映射配置文件中定义的的全部<sql>节点,具体代码如下所示:

  private void sqlElement(List<XNode> list, String requiredDatabaseId) {
    //遍历所有的<sql>节点
    for (XNode context : list) {
      //解析<sql>节点
      //获取<sql>节点的databaseId属性的值
      String databaseId = context.getStringAttribute("databaseId");
      //获取<sql>节点的id属性的值
      String id = context.getStringAttribute("id");
      //获取在当前命名空间的对应的全路径id
      id = builderAssistant.applyCurrentNamespace(id, false);
      //进行数据库ID匹配,如果当前sql片段所属的databaseId和configuration.databaseId相等
      if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) {
        //将匹配的sql片段,放入Configuration.sqlFragments集合中
        sqlFragments.put(id, context);
      }
    }
  }

这个章节比较简单,所以这里讲一下Configuration.StricMap类:

这个类在存储解析Mapper映射配置文件中之后的对象中,起到了至关重要的作用,具体体现如下所示:

 /**
   * key = Cache的ID (默认是映射文件的namespace)
   * value = Cache对象(二级缓存)
   */
  protected final Map<String, Cache> caches = new StrictMap<>("Caches collection");
  /**
   * 用于存储ResultMap
   */
  protected final Map<String, ResultMap> resultMaps = new StrictMap<>("Result Maps collection");
  protected final Map<String, ParameterMap> parameterMaps = new StrictMap<>("Parameter Maps collection");
 /**
   * 用于存储KeyGenerator
   */
  protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<>("Key Generators collection");
 /**
   * 用于存储sql片段
   */
  protected final Map<String, XNode> sqlFragments = new StrictMap<>("XML fragments parsed from previous mappers");

为什么要使用StricMap完成以上信息的存储,那取决于StricMapput()方法的一些特性,具体如下所示:

 public V put(String key, V value) {
      //如果检测到重复的key直接抛出异常
      if (containsKey(key)) {
        throw new IllegalArgumentException(name + " already contains value for " + key
            + (conflictMessageProducer == null ? "" : conflictMessageProducer.apply(super.get(key), value)));
      }
      //如果没有重复的key则添加key以及value
      //同时根据key产生shortKey
      if (key.contains(".")) {
        //按照“.”将key切分成数组,然后将数组的最后一项作为shortKey
        final String shortKey = getShortName(key);
        //如果不包含指定shortKey,则添加该键值对
        if (super.get(shortKey) == null) {
          super.put(shortKey, value);
        } else {
          //如果该shortKey已经存在,则将value修改成Ambiguity对象
          super.put(shortKey, (V) new Ambiguity(shortKey));
        }
      }
      //如果没有重复的key则添加key以及value(这是全面)
      return super.put(key, value);
    }

在调用StricMap.put()方法的时候,首先要检查key是否存在,如果存在会直接报错。

那么为什么在检查key存在之后就直接报错呢?原因如下所示:

  1. 传入的key等于调用builderAssistant.applyCurrentNamespace(id, false)返回的值,在调用builderAssistant.applyCurrentNamespace()方法参入的id是节点的id,例如:whereSqlElement就是传入的id值。

    <sql id="whereSqlElement">
        where blog_id = ${idValue}
      </sql>

    因为同一个Mapper.xml中,不用有id相同的标签,如上代码块,而万一Mapper配置文件中真的有两段一样的代码,就应该提前报错告诉开发人员去更改,所以StricMap.put()中的首先检测key是否存在,不存在就直接报错。

  2. StricMap.put()会向集合中加入两条数据,一条是全名key,一条是shorKey名。



Mybatis源码分析   核心处理层      Mybatis源码分析

本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!