• Mybatis之trim标签的理解


    最近在学Mybatis,在学到动态sql的trim标签时,很迷惑。不知所以然。看别人的博客和论坛里的解释,太宽泛,还是不能理解:

    trim元素的主要功能是可以在自己包含的内容前加上某些前缀,也可以在其后加上某些后缀,与之对应的属性是prefix和suffix;可以把包含内容的首部某些内容覆盖,即忽略,也可以把尾部的某些内容覆盖,对应的属性是prefixOverrides和suffixOverrides;正因为trim有这样的功能,所以我们也可以非常简单的利用trim来代替where元素的功能。

    例1:

     1 <select id="selectUsersTrim" resultMap="resultListUsers" parameterType="Users">
     2       select * from users
     3       <trim prefix="where" prefixOverrides="and">
     4           <if test="name!=null">
     5               name=#{name}
     6           </if>
     7           <if test="address!=null">
     8                and address=#{address}
     9           </if>
    10       </trim>    
    11   </select>

    可以看到后台打印的sql:

    例2:

     1   <select id="selectUsersTrim" resultMap="resultListUsers" parameterType="Users">
     2       select * from users
     3       <trim prefix="where" prefixOverrides="and">
     4           <if test="name!=null">
     5               or name=#{name}
     6           </if>
     7           <if test="address!=null">
     8                and address=#{address}
     9           </if>
    10       </trim>    
    11   </select>

    后台报错:

    怎么办呢?遇到问题,当然要想办法解决。灵光一闪,我不是把源代码绑定到了mybatis的jar上了吗?对,那就看源代码,源代码还能debug呢,更方便。

    trim标签的java方法调用栈轨迹:(只写出关键的几个)

    org.apache.ibatis.scripting.xmltags.TrimSqlNode.apply()

    org.apache.ibatis.scripting.xmltags.TrimSqlNode.FilteredDynamicContext.applyAll()

    org.apache.ibatis.scripting.xmltags.TrimSqlNode.FilteredDynamicContext.applyPrefix()

    applyPrefix()的源代码:

    代码块1:

     1  private void applyPrefix(StringBuilder sql, String trimmedUppercaseSql) {
     2       if (!prefixApplied) {
     3         prefixApplied = true;
     4         if (prefixesToOverride != null) {
     5           for (String toRemove : prefixesToOverride) {
     6             if (trimmedUppercaseSql.startsWith(toRemove)) {
     7               sql.delete(0, toRemove.trim().length());
     8               break;
     9             }
    10           }
    11         }
    12         if (prefix != null) {
    13           sql.insert(0, " ");
    14           sql.insert(0, prefix);
    15         }
    16       }
    17     }
    prefixApplied的值:初始是false.从下面代码内部类TrimSqlNode.FilteredDynamicContext的构造器可以看出:
    代码块2:
    1 public FilteredDynamicContext(DynamicContext delegate) {
    2       super(configuration, null);
    3       this.delegate = delegate;
    4       this.prefixApplied = false;
    5       this.suffixApplied = false;
    6       this.sqlBuffer = new StringBuilder();
    7     }
    List集合类型prefixesToOverride :trim标签的属性prefixOverrides,
    String类型prefix:trim标签的属性prefix.
    trimmedUppercaseSql:trim标签的子标签里的sql语句.
    见下面代码:(TrimSqlNode的成员变量):
    代码块3:
    1   private String prefix;
    2   private String suffix;
    3   private List<String> prefixesToOverride;
    4   private List<String> suffixesToOverride;

    现在我们在回看代码块1,会发现整个方法的大致意思:当trim标签prefixOverrides属性不为空时,遍历prefixOverrides集合的值,并且用trim标签里第一个子标签(比如if标签)的sql的语句头去匹配prefixOverrides集合的元素值,一旦匹配成功,则将第一个子标签的sql语句匹配的元素删掉,继续向下运行,判断prefix属性是否有值,若有值,在将prefix的值放到第一个子标签的sql语句开头。

    所以,例2,将prefixOverrides的值改为 or  或者 and|or 就行了。 

    好吧,就是这样,可能是自己的语言表达能力不够水平,感觉还是看代码来的精确 :)

    最后在总结下吧:

    trim标签的prefixOverrides和prefix分两步骤:

    1.如果prefixOverrides有元素,拿元素去匹配 第一个子标签sql语句,若匹配上,就删掉sql语句的匹配部分,跳到2

    2.如果prefix有值,就在  第一个子标签sql语句 的最前面加上 prefix的值。

     
  • 相关阅读:
    根据经纬度获取距离
    获取本浏览器经纬度坐标
    仿造mongodb的存储方式存一些假数据
    ty修饰符 public private static
    ty 枚举类型
    限制字符串的选择
    typeScript类型别名
    ty 函数的讲解
    ty数组的讲解
    接口的讲解
  • 原文地址:https://www.cnblogs.com/westward/p/6706570.html
Copyright © 2020-2023  润新知