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 中完成复核条件的查询。

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