MyBatis 的動態 SQL 是其最強大的特性之一,徹底免除了手動拼接 SQL 字串的痛苦與風險。本文系統梳理四大核心標籤——<if>、<choose/when/otherwise>、<trim/where/set>、<foreach> 的語法、適用場景與實戰範例,並分享避坑技巧。
最常用的標籤,根據運算式動態決定是否納入 SQL 片段。
<select id="getCategory" resultMap="EshopCategory" parameterType="EshopShopCategory">
SELECT * FROM CATEGORY t
WHERE 1=1
<if test="shopCategoryId != null">
AND t.category_id = #{shopCategoryId}
</if>
<if test="shopCategoryName != null">
AND t.CATEGORY_NAME LIKE CONCAT('%', #{shopCategoryName}, '%')
</if>
<if test="shopId == null">
AND t.shop_id = 0
</if>
ORDER BY SEQUENCE_NO
</select>
技巧:WHERE 1=1 讓後續 AND 直接拼接,避免首條件為空導致語法錯誤。
<insert id="addCategory" parameterType="EshopCategory">
INSERT INTO CATEGORY (
<if test="shopCategoryName != null and shopCategoryName != ''"> shop_category_name, </if>
<if test="shopId != null and shopId != ''"> shop_id, </if>
ADD_TIME
) VALUES (
<if test="shopCategoryName != null and shopCategoryName != ''"> #{shopCategoryName, jdbcType=VARCHAR}, </if>
<if test="shopId != null and shopId != ''"> #{shopId, jdbcType=NUMERIC}, </if>
CURRENT_TIMESTAMP
)
</insert>
適用於資料庫欄位有預設值、不想寫入 NULL 的場景。
<update id="updateProductCategory" parameterType="EshopShopCategory">
UPDATE CATEGORY t
SET
<if test="shopCategoryName != null"> t.category_name = #{shopCategoryName, jdbcType=VARCHAR}, </if>
<if test="updateUser != null"> t.update_user = #{updateUser, jdbcType=VARCHAR}, </if>
t.update_time = CURRENT_TIMESTAMP
WHERE t.shop_category_id = #{shopCategoryId, jdbcType=NUMERIC}
</update>
只更新傳入非 null 的欄位,極大簡化「部分更新」邏輯。
類似 Java switch-case-default,只會進入第一個符合條件的 <when>,若皆不符合則進入 <otherwise>。
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG WHERE state = 'ACTIVE'
<choose>
<when test="title != null"> AND title LIKE #{title} </when>
<when test="author != null and author.name != null"> AND author_name LIKE #{author.name} </when>
<otherwise> AND featured = 1 </otherwise>
</choose>
</select>
適用場景:互斥查詢規則(如:依標題搜尋、或依作者搜尋、或預設推薦),避免多條件疊加。
動態條件最怕產生 WHERE 後無條件、或 SET 後有多餘逗號。MyBatis 提供三標籤自動修剪。
<select id="findActiveBlogLike" resultType="Blog">
SELECT * FROM BLOG
<where>
<if test="state != null"> state = #{state} </if>
<if test="title != null"> AND title LIKE #{title} </if>
<if test="author != null and author.name != null"> AND author_name LIKE #{author.name} </if>
</where>
</select>
WHERE。AND/OR 開頭 → 自動移除。<trim prefix="WHERE" prefixOverrides="AND |OR ">
<if test="state != null"> AND state = #{state} </if>
...
</trim>
等價於 <where>,但可自訂 prefix/suffix/prefixOverrides/suffixOverrides,適用於非標準 SQL 片段。
<update id="updateAuthorIfNecessary">
UPDATE Author
<set>
<if test="username != null"> username = #{username}, </if>
<if test="password != null"> password = #{password}, </if>
<if test="email != null"> email = #{email}, </if>
<if test="bio != null"> bio = #{bio} </if>
</set>
WHERE id = #{id}
</update>
SET 關鍵字(避免 UPDATE ... SET WHERE 語法錯誤)。支援 List、陣列、Map 等集合,自動處理分隔符、括號,極度智慧。
| 屬性 | 說明 |
|---|---|
collection | 集合參數名(List→list、陣列→array、或自訂 @Param 名) |
item | 迭代元素別名 |
index | 索引別名(List 為索引、Map 為 Key) |
open | 起始字串(如 () |
separator | 分隔符(如 ,) |
close | 結束字串(如 )) |
<select id="selectPostIn" resultType="domain.blog.Post">
SELECT * FROM POST P
WHERE ID IN
<foreach item="item" index="index" collection="list" open="(" separator="," close=")">
#{item}
</foreach>
</select>
傳入 List<Long> ids,MyBatis 自動生成 IN (?, ?, ?) 並綁定參數,防止 SQL 注入。
<select id="selectNumInOrder" resultType="String">
SELECT COUNT(0) FROM `order` a
LEFT JOIN item b ON a.ORDER_ID = b.ORDER_ID
WHERE a.STATUS IN ('1','2','3','5')
<if test="list != null and list.size() > 0">
AND b.PHONE_NUM IN
<foreach item="numberList" collection="list" open="(" separator="," close=")">
#{numberList.num}
</foreach>
</if>
</select>
List/陣列參數時,MyBatis 自動以 list/array 為鍵放入 Map;多參數務必用 @Param("xxx") 明確命名。<if test="list != null and list.size() > 0"> 包裹 <foreach>,避免 IN () 語法錯誤。#{ } 使用預編譯參數(安全);${ } 直接字串替換(僅用於不可參數化的識別符,如表名、排序欄位),嚴禁用於使用者輸入值。IN 列表過長(超過 1000)可能導致資料庫解析緩慢,考慮分批或使用臨時表。<sql> 片段再 <include> 組合,保持 Mapper 清晰。| 標籤 | 核心用途 | 典型場景 |
|---|---|---|
<if> | 布林條件納入 | 多條件查詢、動態 INSERT/UPDATE |
<choose/when/otherwise> | 互斥分支 | 單一查詢規則選擇 |
<where> | 自動修剪 WHERE | 動態條件查詢 |
<set> | 自動修剪 SET 尾逗號 | 動態欄位更新 |
<trim> | 自訂修剪規則 | 特殊 SQL 片段組裝 |
<foreach> | 集合遍歷 | IN/NOT IN、批次 INSERT VALUES |
善用這四大標籤,即可覆蓋 95% 以上的動態 SQL 需求,讓 Mapper XML 既安全又優雅。