Edg !動(dòng)態(tài) SQL!牛 !
1. 動(dòng)態(tài)SQL中的元素
動(dòng)態(tài)SQL有什么作用?
“開發(fā)人員在使用JDBC或其他類似的框架進(jìn)行數(shù)據(jù)庫(kù)開發(fā)時(shí),通常都要根據(jù)需求去手動(dòng)拼裝SQL,這是一個(gè)非常麻煩且痛苦的工作,而MyBatis提供的對(duì)SQL語(yǔ)句動(dòng)態(tài)組裝的功能,恰能很好的解決這一麻煩工作。
動(dòng)態(tài)SQL是MyBatis的強(qiáng)大特性之一,MyBatis3采用了功能強(qiáng)大的基于OGNL的表達(dá)式來(lái)完成動(dòng)態(tài)SQL。動(dòng)態(tài)SQL主要元素如下表所示:
2. < if >元素
“在MyBatis中,< if >元素是最常用的判斷語(yǔ)句,它類似于Java中的if語(yǔ)句,主要用于實(shí)現(xiàn)某些簡(jiǎn)單的條件選擇。其基本使用示例如下:
- select * from t_customer where 1=1
- <if test="username !=null and username !=''">
- and username like concat('%',#{username}, '%')
- </if>
- <if test="jobs !=null and jobs !=''">
- and jobs= #{jobs}
- </if>
使用< if >元素對(duì)username和jobs進(jìn)行非空判斷,并動(dòng)態(tài)組裝SQL
“在實(shí)際應(yīng)用中,我們可能會(huì)通過(guò)多個(gè)條件來(lái)精確的查詢某個(gè)數(shù)據(jù)。例如,要查找某個(gè)客戶的信息,可以通過(guò)姓名和職業(yè)來(lái)查找客戶,也可以不填寫職業(yè)直接通過(guò)姓名來(lái)查找客戶,還可以都不填寫而查詢出所有客戶,此時(shí)姓名和職業(yè)就是非必須條件。
3. < choose >及其子元素
- <!--<choose>(<when>、<otherwise>)元素使用 -->
- <select id="findCustomerByNameOrJobs" parameterType="com.nateshao.po.Customer"
- resultType="com.nateshao.po.Customer">
- select * from t_customer where 1=1
- <choose>
- <when test="username !=null and username !=''">
- and username like concat('%',#{username}, '%')
- </when>
- <when test="jobs !=null and jobs !=''">
- and jobs= #{jobs}
- </when>
- <otherwise>
- and phone is not null
- </otherwise>
- </choose>
- </select>
使用< choose >及其子元素依次對(duì)條件進(jìn)行非空判斷,并動(dòng)態(tài)組裝SQL。
4. < when >、< trim >元素
在前面中,映射文件中編寫的SQL后面都加入了“where 1=1”的條件,那么到底為什么要這么寫呢?如果將where后“1=1”的條件去掉,那么MyBatis所拼接出來(lái)的SQL將會(huì)如下所示:
- select * from t_customer where and username like concat('%',?, '%')
可以看出上面SQL語(yǔ)句明顯存在SQL語(yǔ)法錯(cuò)誤,而加入了條件“1=1”后,既保證了where后面的條件成立,又避免了where后面第一個(gè)詞是and或者or之類的關(guān)鍵詞。不過(guò)“where 1=1”這種寫法對(duì)于初學(xué)者來(lái)將不容易理解,并且也不夠雅觀。
針對(duì)上述情況中“where 1=1”,在MyBatis的SQL中就可以使用< where >或< trim >元素進(jìn)行動(dòng)態(tài)處理。
動(dòng)態(tài)SQL處理
- select * from t_customer
- <where>
- <if test="username !=null and username !=''">
- and username like concat('%',#{username}, '%')
- </if>
- <if test="jobs !=null and jobs !=''">
- and jobs= #{jobs}
- </if>
- </where>
< where >元素處理
< where >會(huì)自動(dòng)判斷SQL語(yǔ)句,只有< where >內(nèi)的條件成立時(shí),才會(huì)在拼接SQL中加入where關(guān)鍵字,否則將不會(huì)添加;還會(huì)去除多余的“AND”或“OR”。
- select * from t_customer
- <trim prefix="where" prefixOverrides="and">
- <if test="username !=null and username !=''">
- and username like concat('%',#{username}, '%')
- </if>
- <if test="jobs !=null and jobs !=''">
- and jobs= #{jobs}
- </if>
- </trim>
5. < set >元素
“在Hibernate中,想要更新某個(gè)對(duì)象,就需要發(fā)送所有的字段給持久化對(duì)象,這種想更新的每一條數(shù)據(jù)都要將其所有的屬性都更新一遍的方法,其執(zhí)行效率是非常差的。為此,在MyBatis中可以使用動(dòng)態(tài)SQL中的< set >元素進(jìn)行處理:
- <!-- <set>元素 -->
- <update id="updateCustomer" parameterType="com.nateshao.po.Customer">
- update t_customer
- <set>
- <if test="username !=null and username !=''">
- username=#{username},
- </if>
- <if test="jobs !=null and jobs !=''">
- jobs=#{jobs},
- </if>
- <if test="phone !=null and phone !=''">
- phone=#{phone},
- </if>
- </set>
- where id=#{id}
- </update>
使用< set >和< if >元素對(duì)username和jobs進(jìn)行更新判斷,并動(dòng)態(tài)組裝SQL。這樣就只需要傳入想要更新的字段即可
代碼實(shí)現(xiàn):
6. < foreach >元素
假設(shè)如下需求:在一個(gè)客戶表中有1000條數(shù)據(jù),現(xiàn)在需要將id值小于100的客戶信息全部查詢出來(lái),這要怎么做呢?
一條一條的查詢 :那如果要查詢1000條數(shù)據(jù)呢,豈不是很累?
在Java中用for循環(huán)查詢 :考慮過(guò)N條查詢語(yǔ)句時(shí)的查詢效率了嗎?
針對(duì)上述需求,理想的解決方法就是使用MyBatis中動(dòng)態(tài)SQL的< foreach >元素進(jìn)行處理。其基本使用示例如下所示:
- <!--<foreach>元素使用 -->
- <select id="findCustomerByIds" parameterType="List"
- resultType="com.nateshao.po.Customer">
- select * from t_customer where id in
- <foreach item="id" index="index" collection="list" open="("
- separator="," close=")">
- #{id}
- </foreach>
- </select>
關(guān)于上述示例中< foreach >元素中使用的幾種屬性的描述具體如下:
- item:配置的是循環(huán)中當(dāng)前的元素。
- index:配置的是當(dāng)前元素在集合的位置下標(biāo)。
- collection:配置的list是傳遞過(guò)來(lái)的參數(shù)類型(首字母小寫),它可以是一個(gè)array、list(或collection)、Map集合的鍵、POJO包裝類中數(shù)組或集合類型的屬性名等。
- open和close:配置的是以什么符號(hào)將這些集合元素包裝起來(lái)。
- separator:配置的是各個(gè)元素的間隔符。
在使用< foreach >時(shí)最關(guān)鍵也是最容易出錯(cuò)的就是collection屬性,該屬性是必須指定的,而且在不同情況下,該屬性的值是不一樣的。主要有以下3種情況:
- 如果傳入的是單參數(shù)且參數(shù)類型是一個(gè)數(shù)組或者List的時(shí)候,collection屬性值分別為array和list(或collection)。
- 如果傳入的參數(shù)是多個(gè)的時(shí)候,就需要把它們封裝成一個(gè)Map了,當(dāng)然單參數(shù)也可以封裝成Map集合,這時(shí)候collection屬性值就為Map的鍵。
- 如果傳入的參數(shù)是POJO包裝類的時(shí)候,collection屬性值就為該包裝類中需要進(jìn)行遍歷的數(shù)組或集合的屬性名。
7. < bind >元素
在入門案例中模糊查詢的SQL語(yǔ)句中?
- select * from t_customer where username like '%${value}%'
上述SQL語(yǔ)句有什么不妥?
- 如果使用“${}”進(jìn)行字符串拼接,則無(wú)法防止SQL注入問題;
- 如果改用concat函數(shù)進(jìn)行拼接,則只針對(duì)MySQL數(shù)據(jù)庫(kù)有效;
- 如果改用“||”進(jìn)行字符串拼接,則只針對(duì)Oracle數(shù)據(jù)庫(kù)有效。
小提示:這樣,映射文件中的SQL就要根據(jù)不同的情況提供不同形式的實(shí)現(xiàn),這顯然是比較麻煩的,且不利于項(xiàng)目的移植。為了減少這種麻煩,就可以使用MyBatis的< bind >元素來(lái)解決這一問題。
MyBatis的< bind >元素可以通過(guò)OGNL表達(dá)式來(lái)創(chuàng)建一個(gè)上下文變量,其使用方式如下:
- <!--<bind>元素的使用:根據(jù)客戶名模糊查詢客戶信息 -->
- <select id="findCustomerByName" parameterType="com.nateshao.po.Customer"
- resultType="com.nateshao.po.Customer">
- <!--_parameter.getUsername()也可直接寫成傳入的字段屬性名,即username -->
- <bind name="pattern_username" value="'%'+_parameter.getUsername()+'%'"/>
- select * from t_customer
- where
- username like #{pattern_username}
- </select>
_parameter.getUsername()表示傳遞進(jìn)來(lái)的參數(shù)(也可以直接寫成對(duì)應(yīng)的參數(shù)變量名,如username)。
總結(jié)
好了,最后我們可以了解常用動(dòng)態(tài)SQL元素的主要作用,并能夠掌握這些元素在實(shí)際開發(fā)中如何使用。因?yàn)榍в鹪趯?shí)際開發(fā)MyBatis框架中,這些動(dòng)態(tài)SQL元素的使用十分重要,熟練的掌握它們能夠極大的提高開發(fā)效率。