ibatis筆記(一):組合條件查詢SQL語句

原文鏈接: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 中完成複覈條件的查詢。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章