原文鏈接:https://www.cnblogs.com/brandon988/p/6038383.html
近來工作中用到了 ibatis 技術,主要用來完成動態條件的查詢,深感這種一勞永逸的書寫方式確實很genius。不過因爲是在使用的過程中照貓畫虎,沒有深入系統地去研究,所以這幾天遇到了一個很棘手的問題。在網上查了很久也沒有太清楚解決辦法,後來終於找到了一篇和我的問題很類似的介紹,在此要多謝原作者的 ibatis傳入數組或List類型參數小結。
之前使用 ibatis 來寫SQL查詢語句,無非就是 WHERE 後的條件是並聯的。也就是說,一些動態的條件用 AND 相連。下面舉一個例子:
<select id="SelectEnergyCost" parameterClass="Hashtable" resultClass="EnergyCostData">
<![CDATA[
SELECT * FROM UNITCOMP
]]>
<dynamic prepend="WHERE">
<isParameterPresent>
<isNotEmpty prepend="AND" property="DATEFROM" >
<![CDATA[
DATETIME >= TO_DATE( #DATEFROM#,'YYYY-MM-DD HH24:MI:SS')
]]>
</isNotEmpty>
<isNotEmpty prepend="AND" property="DATETO" >
<![CDATA[
DATETIME <= TO_DATE( #DATETO#,'YYYY-MM-DD HH24:MI:SS')
]]>
</isNotEmpty>
<isNotEmpty prepend="AND" property="IGROUPID" >
IGROUPID = #IGROUPID#
</isNotEmpty >
<isNotEmpty prepend="AND" property="CONDITION1" >
CONDITION1 LIKE '%' || #CONDITION1# || '%'
</isNotEmpty >
<isNotEmpty prepend="AND" property="CONDITION2" >
CONDITION2 LIKE '%' || #CONDITION2# || '%'
</isNotEmpty >
<isNotEmpty prepend="AND" property="CONDITION3" >
CONDITION3 LIKE '%' || #CONDITION3# || '%'
</isNotEmpty >
</isParameterPresent>
</dynamic>
ORDER BY DATETIME DESC
</select>
上面是網上找來的例子,下面是我經過實際調試的例子:
<select id="queryInfoByCompoundConditions" parameterClass="java.util.Map" resultMap="ConfigResult">
SELECT
<include refid="Config.AllFields"/>
FROM member_config
<dynamic prepend="WHERE" >
<isParameterPresent>
<isNotEmpty prepend="AND" property="startDt" >
<![CDATA[
start_dt <= #startDt#
]]>
</isNotEmpty>
<isNotEmpty prepend="AND" property="endDt" >
<![CDATA[
end_dt >= #endDt#
]]>
</isNotEmpty>
<isNotEmpty prepend="AND" property="benefitId">
benefit_id = #benefitId#
</isNotEmpty>
<isNotEmpty prepend="AND" property="itemId">
item_id = #itemId#
</isNotEmpty>
<isNotEmpty prepend="AND" property="benefitStatus">
status = #benefitStatus#
</isNotEmpty>
<isNotEmpty prepend="AND" property="creator">
creator = #creator#
</isNotEmpty>
<isNotEmpty prepend="AND" property="benefitName">
name LIKE concat('%', #benefitName#, '%')
</isNotEmpty>
</isParameterPresent>
</dynamic>
ORDER BY start_dt DESC
</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>這個標籤。通過不斷迭代,就可以實現我們所需要的功能。問題在於,這種方法的格式我不太清楚是怎樣的,包括入參應該以什麼樣的形式傳入。過程就不廢話了,通過看開頭提到的文章,我最終還是解決了這個問題,下面先直接給出結果:
<select id="SelectEnergyCost2" parameterClass="Hashtable" resultClass="EnergyCostData">
<![CDATA[
SELECT * FROM UNITCOMP
]]>
<dynamic prepend="WHERE">
<isParameterPresent>
<isNotEmpty prepend="AND" property="DATEFROM" >
<![CDATA[
DATETIME >= TO_DATE( #DATEFROM#,'YYYY-MM-DD HH24:MI:SS')
]]>
</isNotEmpty>
<isNotEmpty prepend="AND" property="DATETO" >
<![CDATA[
DATETIME <= TO_DATE( #DATETO#,'YYYY-MM-DD HH24:MI:SS')
]]>
</isNotEmpty>
<isNotEmpty prepend="AND" property="GROUPIDS">
IGROUPID IN
<iterate open="(" close=")" conjunction="," property="GROUPIDS" >
$GROUPIDS[]$
</iterate >
</isNotEmpty>
<isNotEmpty prepend="AND" property="CONDITION1" >
CONDITION1 LIKE '%' || #CONDITION1# || '%'
</isNotEmpty >
<isNotEmpty prepend="AND" property="CONDITION2" >
CONDITION2 LIKE '%' || #CONDITION2# || '%'
</isNotEmpty >
<isNotEmpty prepend="AND" property="CONDITION3" >
CONDITION3 LIKE '%' || #CONDITION3# || '%'
</isNotEmpty >
</isParameterPresent>
</dynamic>
ORDER BY DATETIME DESC
</select>
重點看IGROUPID這一部分。<iterate>就相當於一個循環語句,首先要定義循環部分開頭和結尾,用open和close屬性分別定義爲“(”和“)”,連接符用conjunction屬性定義爲“,”,最後入參是GROUPIDS。語句中同樣還是用#GROUPIDS#來接收入參。
那麼這裏的入參究竟是什麼呢?我們自然能想到它要麼是一個數組,要麼是一個集合。在 ibatis 的規定裏,入參必須是array或List<>,也就是數組。所以在語句中,接收入參的部分要是一個能夠接收數組的形式。在這裏我嘗試了很久,最後發現,不能直接用GROUPIDS,而是要寫GROUPIDS[]。這樣就會在每一個迭代循環中,分別只用數組的一個元素來賦值。至於爲什麼用$而非#,我沒有嘗試,大家可以自己試一下#有沒有問題。
xml這部分這樣就解決了。前臺的代碼相對也很簡單,只要定義一個數組放進Hashtable就可以了。我採取的是以下的形式(C#),大家可以當做參考。
Hashtable ht = new Hashtable();
int iGroupID = int.Parse(this.ddlcGroup.SelectedValue);
List<int> ilGroupIDs = new List<int>();
switch (iGroupID)
{
case 1 :
{
ilGroupIDs.Add(10001);
ilGroupIDs.Add(10002);
break;
}
case 2 :
{
ilGroupIDs.Add(10003);
ilGroupIDs.Add(10004);
break;
}
case 3 :
{
ilGroupIDs.Add(10005);
ilGroupIDs.Add(10006);
ilGroupIDs.Add(10007);
break;
}
default :
break;
}
ht.Add("GROUPIDS", ilGroupIDs.ToArray());
ht.Add("DATEFROM", this.txtDateFrom.Text);
ht.Add("DATETO", this.txtDateTo.Text);
其中第28行,把數組放進Hashtable的時候,因爲ilGroupIDs本身就是一個數組了,所以可能不需要.ToArray()方法,但是我沒有嘗試。感興趣的朋友可以自己深入探討這其中的一些細節。
通過上面的示例,就可以在 ibatis 中完成複覈條件的查詢。