轉自:https://www.cnblogs.com/westward/p/6706570.html
最近在學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的值。