使用ibatis完成復合條件SQL語句的查詢


[ 本文原創發表於cnblogs : 布藍燈 ]

    近來工作中用到了 ibatis 技術,主要用來完成動態條件的查詢,深感這種一勞永逸的書寫方式確實很genius。不過因為是在使用的過程中照貓畫虎,沒有深入系統地去研究,所以這幾天遇到了一個很棘手的問題。在網上查了很久也沒有太清楚解決辦法,后來終於找到了一篇和我的問題很類似的介紹,在此要多謝原作者的 ibatis傳入數組或List類型參數小結

    之前使用 ibatis 來寫SQL查詢語句,無非就是 WHERE 后的條件是並聯的。也就是說,一些動態的條件用 AND 相連。下面舉一個例子

 1 <select id="SelectEnergyCost" parameterClass="Hashtable" resultClass="EnergyCostData">
 2     <![CDATA[
 3  SELECT * FROM UNITCOMP  4     ]]>
 5     <dynamic prepend="WHERE">
 6         <isParameterPresent>
 7             <isNotEmpty prepend="AND" property="DATEFROM" >
 8                 <![CDATA[ 
 9  DATETIME >= TO_DATE( #DATEFROM#,'YYYY-MM-DD HH24:MI:SS') 10                 ]]>
11             </isNotEmpty>
12             <isNotEmpty prepend="AND" property="DATETO" >
13                 <![CDATA[ 
14  DATETIME <= TO_DATE( #DATETO#,'YYYY-MM-DD HH24:MI:SS') 15                 ]]>
16             </isNotEmpty>
17             <isNotEmpty prepend="AND" property="IGROUPID" >
18  IGROUPID = #IGROUPID# 19             </isNotEmpty >
20             <isNotEmpty prepend="AND" property="CONDITION1" >
21  CONDITION1 LIKE '%' || #CONDITION1# || '%' 22             </isNotEmpty >
23             <isNotEmpty prepend="AND" property="CONDITION2" >
24  CONDITION2 LIKE '%' || #CONDITION2# || '%' 25             </isNotEmpty >
26             <isNotEmpty prepend="AND" property="CONDITION3" >
27  CONDITION3 LIKE '%' || #CONDITION3# || '%' 28             </isNotEmpty >
29         </isParameterPresent>
30     </dynamic>
31  ORDER BY DATETIME DESC 32 </select>

    (涉及工作內容,部分地方命名做了一些通用化處理)

    簡單說一下上面這段,這樣即使對 ibatis 不熟悉的人也能更清楚看懂。

    首先是<select>標簽,parameterClass是入參,也就是接下來的查詢語句中將要用到的各個字段的值,resultClass是輸出,也就是輸出的查詢結果。輸入輸出都可以是很靈活的格式,可以只是一個整型變量,也可以是一個數據集。在我的例子中,輸出是字段與值相對應的Hashtable,輸出則是一個數據集,字段與所查詢的表一致。

    之后就可以直接寫SQL語句了。假如不涉及到動態條件,那么就可以直接寫完整的SQL語句。因為程序本身是xml格式,為了避免SQL語句和xml的字符沖突,可以用“<![CDATA[”和“]]>”把SQL括起來。

    如果涉及到了動態條件,就要用到<dynamic>標簽。ibatis 中可選的標簽有很多,我這里用的是<isNotEmpty>,主要也是因為平台原有的一些語句也都是用的這個,我就照着搬下來了。具體其他標簽有什么區別,大家如果有需要,可以自行研究。<isNotEmpty>標簽的屬性看一眼也很清晰,prepend是指在接下來的語句之前要加這個詞,property是入參。因為是動態的,所以當入參為空的時候,prepend的內容會自動省略,所以入參為空時,在這個<isNotEmpty>中的所有內容都不會添加到語句中,包括prepend。

    有多個條件就添加多個<isNotEmpty>就可以了。接下來的SQL語句就正常寫,需要賦值為入參的地方用“#入參名#”。這里除了#還可以用$,我沒有具體研究過,區別似乎是二者對於入參的數據類型要求不一樣,並且$的安全性較低。感興趣可以深入研究一下。

    所以上面這段程序拼接之后就是:

SELECT * FROM UNITCOMP WHERE DATETIME >= TO_DATE( #DATEFROM#,'YYYY-MM-DD HH24:MI:SS' ) AND DATETIME <= TO_DATE( #DATETO#,'YYYY-MM-DD HH24:MI:SS' ) 

AND IGROUPID = #IGROUPID# AND CONDITION1 LIKE '%#CONDITION1#%' AND CONDITION2 LIKE '%#CONDITION2#%' AND CONDITION3 LIKE '%#CONDITION3#%'

 

 

    那么接下來就是我遇到的問題了。在上面的查詢語句中,我們需要IGROUPID傳進來一個固定的值。但是如果遇到IGROUPID可以取多個值的話,該怎么辦呢?也就是下面這段SQL:

SELECT * FROM UNITCOMP WHERE DATETIME >= TO_DATE( #DATEFROM#,'YYYY-MM-DD HH24:MI:SS' ) AND DATETIME <= TO_DATE( #DATETO#,'YYYY-MM-DD HH24:MI:SS' ) 
AND IGROUPID IN ( #IGROUPID1#, #IGROUPID2# ) AND CONDITION1 LIKE '%#CONDITION1#%' AND CONDITION2 LIKE '%#CONDITION2#%' AND CONDITION3 LIKE '%#CONDITION3#%'

 

    重點看IGROUPID這一段,我們能夠想到的解決辦法可能有這么幾種:

    1、IGROUPID = IGROUPID1 OR IGROUPID = IGROUPID3 OR IGROUPID = IGROUPID3 ...

    2、IGROUPID IN ( IGROUPID1, IGROUPID2, ... )

    3、IGROUPID IN ( IGROUPIDS )。其中 IGROUPIDS為IGROUPID1, IGROUPID2, ... 的集合

    通過比較,當然是第三種方式最優。既然是動態語句,我們就希望它的擴展性能達到最佳,在前台只需要定義了IGROUPS集合,不管集合中有幾個元素,我們都可以一次性傳進來。

 

    但在解決問題的過程中,以上幾種方式我都嘗試過,都沒有很好地成功。實際操作的時候會出現一些問題,不如想象中那么清晰明了,比如第一種,雖然傳參數簡單了,但是這些OR語句拼接好以后外面還需要加一對括號,因為這個條件是在復合條件中,它還需要考慮到與語句其他部分的銜接。而動態添加括號又會很麻煩。

    在查找資料的過程中,我發現了<iterate>這個標簽。通過不斷迭代,就可以實現我們所需要的功能。問題在於,這種方法的格式我不太清楚是怎樣的,包括入參應該以什么樣的形式傳入。過程就不廢話了,通過看開頭提到的文章,我最終還是解決了這個問題,下面先直接給出結果:

 1 <select id="SelectEnergyCost2" parameterClass="Hashtable" resultClass="EnergyCostData">
 2     <![CDATA[
 3     SELECT * FROM UNITCOMP
 4     ]]>
 5     <dynamic prepend="WHERE">
 6         <isParameterPresent>
 7             <isNotEmpty prepend="AND" property="DATEFROM" >
 8                 <![CDATA[ 
 9                 DATETIME >= TO_DATE( #DATEFROM#,'YYYY-MM-DD HH24:MI:SS')
10                 ]]>
11             </isNotEmpty>
12             <isNotEmpty prepend="AND" property="DATETO" >
13                 <![CDATA[ 
14                 DATETIME <= TO_DATE( #DATETO#,'YYYY-MM-DD HH24:MI:SS')
15                 ]]>
16             </isNotEmpty>
17             <isNotEmpty prepend="AND" property="GROUPIDS">
18                 IGROUPID IN
19                 <iterate open="(" close=")" conjunction="," property="GROUPIDS" >
20                     $GROUPIDS[]$
21                 </iterate >
22             </isNotEmpty>
23             <isNotEmpty prepend="AND" property="CONDITION1" >
24                 CONDITION1 LIKE '%' || #CONDITION1# || '%'
25             </isNotEmpty >
26             <isNotEmpty prepend="AND" property="CONDITION2" >
27                 CONDITION2 LIKE '%' || #CONDITION2# || '%'
28             </isNotEmpty >
29             <isNotEmpty prepend="AND" property="CONDITION3" >
30                 CONDITION3 LIKE '%' || #CONDITION3# || '%'
31             </isNotEmpty >
32         </isParameterPresent>
33     </dynamic>
34     ORDER BY DATETIME DESC
35 </select>

 

    重點看IGROUPID這一部分。<iterate>就相當於一個循環語句,首先要定義循環部分開頭和結尾,用open和close屬性分別定義為“(”和“)”,連接符用conjunction屬性定義為“,”,最后入參是GROUPIDS。語句中同樣還是用#GROUPIDS#來接收入參。

    那么這里的入參究竟是什么呢?我們自然能想到它要么是一個數組,要么是一個集合。在 ibatis 的規定里,入參必須是array或List<>,也就是數組。所以在語句中,接收入參的部分要是一個能夠接收數組的形式。在這里我嘗試了很久,最后發現,不能直接用GROUPIDS,而是要寫GROUPIDS[]。這樣就會在每一個迭代循環中,分別只用數組的一個元素來賦值。至於為什么用$而非#,我沒有嘗試,大家可以自己試一下#有沒有問題。

    xml這部分這樣就解決了。前台的代碼相對也很簡單,只要定義一個數組放進Hashtable就可以了。我采取的是以下的形式(C#),大家可以當做參考。

 1 Hashtable ht = new Hashtable();
 2 int iGroupID = int.Parse(this.ddlcGroup.SelectedValue);
 3 List<int> ilGroupIDs = new List<int>();
 4 switch (iGroupID)
 5 {
 6     case 1 :
 7         {
 8             ilGroupIDs.Add(10001);
 9             ilGroupIDs.Add(10002);
10             break;
11         }
12     case 2 :
13         {
14             ilGroupIDs.Add(10003);
15             ilGroupIDs.Add(10004);
16             break;
17         }
18     case 3 :
19         {
20             ilGroupIDs.Add(10005);
21             ilGroupIDs.Add(10006);
22             ilGroupIDs.Add(10007);
23             break;
24         }
25     default :
26         break;
27     }
28     ht.Add("GROUPIDS", ilGroupIDs.ToArray());
29     ht.Add("DATEFROM", this.txtDateFrom.Text);
30     ht.Add("DATETO", this.txtDateTo.Text);

 

    其中第28行,把數組放進Hashtable的時候,因為ilGroupIDs本身就是一個數組了,所以可能不需要.ToArray()方法,但是我沒有嘗試。感興趣的朋友可以自己深入探討這其中的一些細節。

 

    通過上面的示例,就可以在 ibatis 中完成復核條件的查詢。


免責聲明!

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



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