最近在学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的值。

 

 

 

posted on 2017-04-13 22:32  WesTward  阅读(42437)  评论(0编辑  收藏  举报