動態 SQL 是 MyBatis 的強大特性之一。如果你使用過 JDBC 或其它類似的框架,你應該能理解根據不同條件拼接 SQL 語句有多痛苦,例如拼接時要確保不能忘記添加必要的空格,還要注意去掉列表最后一個列名的逗號。利用動態 SQL,可以徹底擺脫這種痛苦。
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
if
select * from user where 1=1 <if test="id != null and id != ''"> and id = #{id} </if> <if test="name != null and name != ''"> and name = #{name} </if>
為什么使用1=1作為參數都很清楚了,如果沒有1=1且條件if為true,那么拼接的sql語句會變成,是錯誤的拼接
如果沒有匹配又沒有1=1的條件會怎么樣?
select * from user where
如果匹配的只是第二個條件又會怎樣?
select * from user where and name = nameValue
if的多條件使用,即使沒有1=1也不會出現問題,因為此時有一個固定條件,只要確保不出現 where 和 and的連接的情況即可。
<select id="findActiveBlogLike" resultType="Blog"> SELECT * FROM BLOG WHERE state = ‘ACTIVE’ <if test="title != null"> AND title like #{title} </if> <if test="author != null and author.name != null"> AND author_name like #{author.name} </if> </select>
choose、when、otherwise
<select id="testStudayChoose" resultType="User"> select * from `user` <where> <choose> <when test="name != null and name != ''"> and name = #{name} </when> <when test="password != null and password != ''"> and password = #{password} </when> <otherwise> and uid = 1 </otherwise> </choose> </where>
</select>
下面會講到<where>,其實就是代替了where 1=1。
值得注意的是choose標簽相當於switch,滿足第一個條件以后就不會執行其他語句拼接,例如name和password兩個參數都傳也只會執行name。
而otherwise標簽則是相當於default當條件都不滿足時執行的拼接sql。
trim、where、set
<where> </where>
前面幾個例子已經合宜地解決了一個臭名昭著的動態 SQL 問題。現在回到之前的 “if” 示例,這次我們將 where 1=1 設置成動態條件
<select id="findOneSql" resultType="User"> select * from `user` <where> <if test="id != null and id != ''"> and uid = #{uid} </if> <if test="birthday != null"> and birthday = #{birthday} </if> </where> </select>
where標簽在if條件都不滿足時取消的where的條件拼接,若子句的開頭為 “AND” 或 “OR”,where 元素也會將它們去除。
trim標簽也可以完成相同的功能
<select id="findOneSql" resultType="User"> select * from `user` <trim prefix="WHERE" prefixOverrides="AND |OR"> <if test="id != null and id != ''"> and uid = #{uid} </if> <if test="birthday != null"> and birthday = #{birthday} </if> </trim> </select>
值得注意的是trim標簽中的每個屬性
屬性 | 描述 |
---|---|
prefix | 給sql語句拼接的前綴 |
suffix | 給sql語句拼接的后綴 |
prefixOverrides | 去除sql語句前面的關鍵字或者字符,該關鍵字或者字符由prefixOverrides屬性指定,假設該屬性指定為"AND",當sql語句的開頭為"AND",trim標簽將會去除該"AND" |
suffixOverrides | 去除sql語句后面的關鍵字或者字符,該關鍵字或者字符由suffixOverrides屬性指定 |
<set></set>
<update id="updateOneSql"> update `user` <set> <if test="name != null and name != ''"> `name` = #{name}, </if> <if test="birthday != null"> birthday = #{birthday} </if> </set> where uid = #{uid} </update>
<-- 或使用的寫法 -->
<trim prefix="SET" suffixOverrides=","> ... </trim>
foreach
例如使用場景為 uid in () 括號中位多個條件
<select id="setValueListSql" parameterType="java.util.List" resultType="User"> SELECT * from `user` where uid in <foreach collection="list" open="(" separator="," close=")" item="item" index="index"> #{item} </foreach> </select>
- collection:表示傳入過來的參數的數據類型。該參數為必選。要做 foreach 的對象,作為入參時,List 對象默認用 list 代替作為鍵,數組對象有 array 代替作為鍵,Map 對象沒有默認的鍵。當然在作為入參時可以使用 @Param(“keyName”) 來設置鍵,設置 keyName 后,list,array 將會失效。 除了入參這種情況外,還有一種作為參數對象的某個字段的時候。舉個例子:
如果 User 有屬性 List ids。入參是 User 對象,那么這個 collection = “ids” 如果 User 有屬性 Ids ids;其中 Ids 是個對象,Ids 有個屬性 List id;入參是 User 對象,那么 collection = “ids.id”
- 如果傳入的是單參數且參數類型是一個 List 的時候,collection 屬性值為 list
- 如果傳入的是單參數且參數類型是一個 array 數組的時候,collection 的屬性值為 array
- 如果傳入的參數是多個的時候,我們就需要把它們封裝成一個 Map 了,當然單參數也可以封裝成 map。
- item: 循環體中的具體對象。支持屬性的點路徑訪問,如 item.age,item.info.details。具體說明:在 list 和數組中是其中的對象,在 map 中是 value,該參數為必選。(它是每一個元素進行迭代時的別名)
- index:在 list 和數組中,index 是元素的序號;在 map 中,index 是元素的 key。
- open:表示該語句以什么開始
- close:表示該語句以什么結束
- separator:表示在每次進行迭代之間以什么符號作為分隔符
補充
在insert時使用foreach批量插入
insert into user(id,name) values <foreach collection="list" separator="," item="item" index="index"> (#{item.id},#{item.name}) </foreach>