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ò)。
原符號 | < | <= | > | >= | & | ' | " |
---|---|---|---|---|---|---|---|
替換符號 | < |
<= |
> |
>= |
& |
' |
" |
<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前面:
下面的是正確的,我們在兩個(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è)世界希望一切都很快,更快笆怠,但是我希望自己能走好每一步誊爹,寫好每一篇文章,期待和你們一起交流办成。