Mybatis【13】-- Mybatis動態(tài)Sql標(biāo)簽的使用

mybatis有一個(gè)強(qiáng)大的特性金赦,其他框架在拼接sql的時(shí)候要特別謹(jǐn)慎对嚼,比如哪里需要空格,還要注意去掉列表最后一個(gè)列名的逗號漠烧,mybtis的動態(tài)sql可以幫助我們逃離這樣的痛苦掙扎靡砌,那就是動態(tài)SQL.它還可以處理一種情況,當(dāng)你不確定你的參數(shù)不知道是不是為空的時(shí)候通殃,我們不需要在業(yè)務(wù)邏輯中判斷画舌,直接在sql中處理,代碼無比簡潔骗炉。主要的動態(tài)sql標(biāo)簽如下:

  • <if></if>
  • <where></where>(trim,set)
  • <choose></choose>(when, otherwise)
  • <foreach></foreach>

注意事項(xiàng):
在mapper中如果出現(xiàn)大于號(>),小于號(),大于等于號()厕鹃,小于等于號()等,最好需要轉(zhuǎn)換成為實(shí)體符號,這是因?yàn)閙apper是XML文件把将,xml文件本身就含有較多的<>這樣的尖括號忆矛,所以解析的時(shí)候可能會解析出錯(cuò)。

原符號 < <= > >= & ' "
替換符號 &lt; &lt;= &gt; &gt;= &amp; &apos; &quot;

<if>

我們經(jīng)常需要根據(jù)where后面的條件篩選出需要的數(shù)據(jù)洽议,當(dāng)多個(gè)條件拼接的時(shí)候漫拭,我們一般使用<if></if>,如果if里面的條件成立采驻,那么就會使用標(biāo)簽的語句,但是我們可以知道where句子第一個(gè)標(biāo)簽是沒有and的膳叨,而后面的條件都需要and痘系,所以有一種做法是第一個(gè)使用where 1 = 1,這個(gè)條件恒成立临谱,后面的所有子語句都加上and奴璃,如果增加判斷城豁,那么我們只需要加<if>標(biāo)簽就可以了。

    <!-- 動態(tài)sql if標(biāo)簽-->
    <!-- &可以使用and來代替 唱星,注意!=需要連在一起寫-->
    <select id="selectStudentByDynamicSQL" resultType="Student">
        <!--最常用的(動態(tài)參數(shù)) select id,name,age,score from student where name like '%' #{name} '%' -->
        <!-- 下面的是字符串拼接 攒盈,只能寫value哎榴,了解即可僵蛛,容易sql注入迎变,執(zhí)行效率低,不建議使用-->
        select id,name,age,score
        from student
        where 1=1
        <if test="name != null and name != ''">
            and name like '%' #{name} '%'
        </if>
        <if test="age > 0">
            and age > #{age}
        </if>
    </select>

當(dāng)有兩個(gè)查詢條件的時(shí)候驼侠,sql語句是:select * from student where 1=1 and name like '%' ? '%' and age > ?

當(dāng)有一個(gè)查詢條件的時(shí)候:sql語句就變成:select * from student where 1=1 and name like '%' ? '%'

當(dāng)沒有查詢條件的時(shí)候谆吴,sql語句是:
select * from student where 1=1

<if></if>標(biāo)簽需要手動在where后面添加1=1語句句狼,這是因?yàn)槿绻?lt;if>后面的條件都是false的時(shí)候,where后面如果沒有1=1語句鲜锚,sql就剩下一個(gè)空空的where,sql就會報(bào)錯(cuò)。所以在where后面需要加上永真句子1=1旺隙,但是這樣有一個(gè)問題骏令,當(dāng)數(shù)據(jù)量比較大的時(shí)候,會嚴(yán)重影響sql的查詢效率榔袋。

<where></where>,<trim></trim>,<set></set>

使用<where></where>標(biāo)簽凰兑,在有查詢語句的時(shí)候,自動補(bǔ)上where子句吏够,在沒有查詢條件的時(shí)候,不會加上where子句播急,這也就解決了我們上面所涉及到的問題售睹,剩下的就是<if>標(biāo)簽的and子句,第一個(gè)捶枢,<if>片段里面可以不包含and,也可以包含柱蟀,系統(tǒng)會自動去掉and,但是其他的<if>片段里面的and畜眨,必須寫上术瓮,否則會出錯(cuò)。下面的寫法中恬汁,如果name為null辜伟,第二個(gè)if標(biāo)簽中的if也會被去掉,不會報(bào)錯(cuò)导狡。

    <select id="selectStudentByDynamicSQLWhere" resultType="Student">
        <!--最常用的(動態(tài)參數(shù)) select id,name,age,score from student where name like '%' #{name} '%' -->
        <!-- 下面的是字符串拼接 旱捧,只能寫value,了解即可枚赡,容易sql注入,執(zhí)行效率低贪婉,不建議使用-->
        select id,name,age,score
        from student
        <where>
            <if test="name != null and name != ''">
                and name like '%' #{name} '%'
            </if>
            <if test="age > 0">
                and age > #{age}
            </if>
        </where>
    </select>

如果where里面是不規(guī)范的料皇,那我們可以通過<trim></trim>來自定義where元素的功能,<trim>標(biāo)簽主要有以下屬性:

  • prefix:在包含的內(nèi)容前加上前綴,不是百分之百會加,會根據(jù)需要自動加
  • suffix:在包含的內(nèi)容后面加上后綴娜膘,不是百分之百會加,會根據(jù)需要自動加
  • prefixOverrides:可以把包含內(nèi)容的首部某些內(nèi)容忽略(不能自己增加)军洼,不一定會忽略,根據(jù)需要自動忽略
  • suffixOverrides:也可以把包含內(nèi)容的尾部的某些內(nèi)容忽略(不能自己增加)避乏,同上

下面這樣的是錯(cuò)誤的甘桑,當(dāng)傳入的name不為空,而且age大于0的時(shí)候

    <select id="selectStudentByDynamicSQLWhere" resultType="Student">
        select id,name,age,score
        from student
        <trim prefix="where" prefixOverrides="and">
            <if test="name != null and name != ''">
                name like '%' #{name} '%'
            </if>
            <if test="age > 0">
                age > #{age}
            </if>
        </trim>
    </select>

不會自己增加and在第二個(gè)age前面:

image


下面的是正確的,我們在兩個(gè)<if>標(biāo)簽前面都增加了and铆帽,第二個(gè)and會自動去掉:

    <select id="selectStudentByDynamicSQLWhere" resultType="Student">
        select id,name,age,score
        from student
        <trim prefix="where" prefixOverrides="and">
            <if test="name != null and name != ''">
                and name like '%' #{name} '%'
            </if>
            <if test="age > 0">
                and age > #{age}
            </if>
        </trim>
    </select>

下面是后綴模式,prefix="set"表示在整個(gè)語句前面加上前綴set德谅,suffixoverride=","表示每一個(gè)語句后面的后綴","可以被忽略窄做,如果是需要的話愧驱。suffix=" where id = #{id}表示在整個(gè)語句后面增加where id = #{id},:

update user
<trim prefix="set" suffixoverride="," suffix=" where id = #{id} ">
  <if test="name != null and name.length()>0"> name=#{name} , </if>
  <if test="age != null "> age=#{age} ,  </if>
</trim>

當(dāng)然组砚,我們對上面的語句還有動態(tài)解決的方案,那就是<set>標(biāo)簽:

    <update id="updateStudent">
        update student
        <set>
            <!-- 第一個(gè)if標(biāo)簽的逗號一定要有庸汗,最后一個(gè)標(biāo)簽的逗號可以沒有-->
            <if test="name != null"> name=#{name},</if>
            <if test="age != null">age=#{age},</if>
            <if test="score != null"> score=#{score},</if>
        </set>
         where id=#{id}
    </update>

<choose>, <when>, <otherwise>

有時(shí)候,我們只想去匹配第一個(gè)條件改化,或者第一個(gè)條件不匹配的時(shí)候才會去匹配第二個(gè)條件枉昏,不像<where></where>標(biāo)簽里面的<if></if>一樣會去判斷所有的子語句是否可以匹配,而是遇到一個(gè)匹配的就會執(zhí)行跳出<choose></choose>

    <!--    selectStudentByDynamicSQLChoose 類似于switch句旱,滿足后就不會判斷后面的了-->
    <!-- 如果名字不為空晰奖,那么按照名字來查詢,如果名字為空匾南,就按照年齡來查詢,如果沒有查詢條件溯乒,就沒有查詢條件 -->
    <select id="selectStudentByDynamicSQLChoose" resultType="Student">
        <!--最常用的(動態(tài)參數(shù)) select id,name,age,score from student where name like '%' #{name} '%' -->
        select id,name,age,score
        from student
        <where>
            <choose>
                <when test="name != null and name != ''">
                    and name like '%' #{name} '%'
                </when>
                <when test="age > 0">
                    and age > #{age}
                </when>
                <otherwise>
                    and 1 != 1
                </otherwise>
            </choose>
        </where>
    </select>

<choose>標(biāo)簽就像是switch語句,每一個(gè)<when>都像是case,后面默認(rèn)跟上break語句矛纹,只要滿足一個(gè)就不會判斷后面的子語句了光稼,當(dāng)前面所有的<when></when>都不執(zhí)行的時(shí)候,就會執(zhí)行<otherwise></otherwise>標(biāo)簽的內(nèi)容迎献,這個(gè)內(nèi)容也就像是switch語句里面的default腻贰。

foreach

動態(tài)SQL要有一個(gè)比較多的操作是對一個(gè)集合進(jìn)行遍歷,通常是在構(gòu)建IN條件語句的時(shí)候播演。需要注意的點(diǎn):

  • collection 表示需要遍歷的集合類型写烤,array表示需要遍歷的數(shù)組
  • open,close洲炊,separator是對遍歷內(nèi)容的SQL拼接
  • foreach 元素的功能非常強(qiáng)大,它允許你指定一個(gè)集合询微,聲明可以在元素體內(nèi)使用的集合項(xiàng)(item)和索引(index)變量狂巢。它也允許你指定開頭與結(jié)尾的字符串以及在迭代結(jié)果之間放置分隔符。
  • 你可以將任何可迭代對象(如 List藻雌、Set 等)斩个、Map 對象或者數(shù)組對象傳遞給 foreach 作為集合參數(shù)。當(dāng)使用可迭代對象或者數(shù)組時(shí)受啥,index 是當(dāng)前迭代的次數(shù),item 的值是本次迭代獲取的元素叁温。當(dāng)使用 Map 對象(或者 Map.Entry 對象的集合)時(shí)核畴,index 是鍵,item 是值跟束。

1.比如我們需要查找學(xué)生的id為1丑孩,2,3的學(xué)生信息温学,我們不希望分開一次査一個(gè),而是希望將數(shù)組id一次傳進(jìn)去逃延,查出來一個(gè)學(xué)生的集合轧拄。

sql接口可以這樣寫,傳入一個(gè)對象的數(shù)組:

public List<Student>selectStudentByDynamicSQLForeachArray(Object[]studentIds);

sql語句如下,遍歷array數(shù)組的時(shí)候,指定左邊符號是左括號拄丰,右邊是右括號俐末,元素以逗號分隔開:

    <!-- select * from student where id in (1,3) -->
    <select id="selectStudentByDynamicSQLForeachArray" resultType="Student">
        select id,name,age,score
        from student
        <if test="array !=null and array.length > 0 ">
            where id in
            <foreach collection="array" open="(" close=")" item="myid" separator=",">
                #{myid}
            </foreach>
        </if>
    </select>

2.當(dāng)遍歷的是一個(gè)類型為int的list列表時(shí):

public List<Student>selectStudentByDynamicSQLForeachList(List<Integer>studentIds);

sql語句如下,colleaction指定為list:

    <select id="selectStudentByDynamicSQLForeachList" resultType="Student">
        select id,name,age,score
        from student
        <if test="list !=null and list.size > 0 ">
            where id in
            <foreach collection="list" open="(" close=")" item="myid" separator=",">
                #{myid}
            </foreach>
        </if>
    </select>

3.當(dāng)遍歷的是一個(gè)類型為對象的list:

public List<Student>selectStudentByDynamicSQLForeachListStudent(List<Student>students);

sql語句里面與上面相似鹅搪,只是在使用屬性的時(shí)候不太一樣:

<select id="selectStudentByDynamicSQLForeachListStudent" resultType="Student">
        select id,name,age,score
        from student
        <if test="list !=null and list.size > 0 ">
            where id in
            <foreach collection="list" open="(" close=")" item="stu" separator=",">
                #{stu.id}
            </foreach>
        </if>
    </select>

<sql></sql>

用于定義sql片段,方便在其他SQL標(biāo)簽里面復(fù)用恢准,在其他地方復(fù)用的時(shí)候需要使用<include></include>子標(biāo)簽甫题,<sql>可以定義sql的任何部分,所以<include>標(biāo)簽可以放在動態(tài)SQL的任何位置坠非。

    <sql id="selectHead">
        select id,name,age,score
         from student
    </sql>
    <!-- 可讀性比較差 -->
    <select id="selectStudentByDynamicSQLfragment" resultType="Student">
        <include refid="selectHead"></include>
        <if test="list !=null and list.size > 0 ">
            where id in
            <foreach collection="list" open="(" close=")" item="stu" separator=",">
                #{stu.id}
            </foreach>
        </if>
    </select>

動態(tài)sql讓SQL寫起來更加簡潔,減少了很多重復(fù)代碼盟迟,動態(tài)sql之間可以相互拼接,只要符合sql語句規(guī)范即可迫皱。

【作者簡介】
秦懷辖众,公眾號【秦懷雜貨店】作者,技術(shù)之路不在一時(shí)凹炸,山高水長啤它,縱使緩慢,馳而不息蚕键。這個(gè)世界希望一切都很快,更快笆怠,但是我希望自己能走好每一步誊爹,寫好每一篇文章,期待和你們一起交流办成。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市搂漠,隨后出現(xiàn)的幾起案子迂卢,更是在濱河造成了極大的恐慌,老刑警劉巖桐汤,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件而克,死亡現(xiàn)場離奇詭異,居然都是意外死亡怔毛,警方通過查閱死者的電腦和手機(jī)员萍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拣度,“玉大人螃壤,你說我怎么就攤上這事〗钐” “怎么了奸晴?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長幕随。 經(jīng)常有香客問我蚁滋,道長,這世上最難降的妖魔是什么赘淮? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮睦霎,結(jié)果婚禮上梢卸,老公的妹妹穿的比我還像新娘。我一直安慰自己副女,他們只是感情好蛤高,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著碑幅,像睡著了一般戴陡。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上沟涨,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天恤批,我揣著相機(jī)與錄音,去河邊找鬼裹赴。 笑死喜庞,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的棋返。 我是一名探鬼主播延都,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼睛竣!你這毒婦竟也來了晰房?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤射沟,失蹤者是張志新(化名)和其女友劉穎殊者,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體躏惋,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡幽污,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了簿姨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片距误。...
    茶點(diǎn)故事閱讀 40,664評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡簸搞,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出准潭,到底是詐尸還是另有隱情趁俊,我是刑警寧澤,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布刑然,位于F島的核電站寺擂,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏泼掠。R本人自食惡果不足惜怔软,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一择镇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧腻豌,春花似錦家坎、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至苏携,卻和暖如春做瞪,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背兜叨。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工穿扳, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人国旷。 一個(gè)月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓矛物,卻偏偏與公主長得像,于是被迫代替她去往敵國和親跪但。 傳聞我的和親對象是個(gè)殘疾皇子履羞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內(nèi)容