Mybatis之trim標簽的理解


 

轉自: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的值。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM