MyBatis 動態 SQL 是其核心特性之一,通過 XML 標籤或註解動態拼接 SQL 語句,解決了傳統 SQL 中硬編碼條件、重複代碼、參數為空導致語法錯誤等問題,能根據不同參數值自動生成適配的 SQL,靈活應對多條件查詢、動態插入 / 更新等場景。

if 標籤

if 標籤根據參數值判斷是否拼接標籤內的 SQL 片段(參數非空 / 非默認值時生效)。

<if test="條件表達式">
    SQL 片段(如:AND 字段名 = #{參數})
</if>

條件表達式規則:

  • 直接引用參數名(如 userName、age)
  • 支持邏輯運算:==(等於)、!=(不等於)、&&(且)、||(或)、!(非)
  • 字符串判斷:userName != null and userName != ''(非空非空串)
  • 數值判斷:age > 18、status == 1
  • 集合判斷:list != null and list.size() > 0(集合非空且有元素)。
<select id="selectUserByCondition" resultType="User">
    SELECT id, user_name, age, status FROM user
    WHERE 1 = 1 <!-- 佔位符,避免條件都不滿足時 WHERE 後無內容 -->
    <if test="userName != null and userName != ''">
        AND user_name LIKE CONCAT('%', #{userName}, '%')
    </if>
    <if test="age != null and age > 0">
        AND age = #{age}
    </if>
</select>

choose、when、otherwise 標籤

多條件分支選擇,僅執行第一個滿足條件的 <when>,若所有 <when> 不滿足,執行 <otherwise>(可選)。

<choose>
    <when test="條件1">SQL 片段1</when>
    <when test="條件2">SQL 片段2</when>
    <otherwise>默認 SQL 片段</otherwise>
</choose>

<select id="selectUserByChoose" resultType="User">
    SELECT id, user_name, age, status FROM user
    <where>
        <choose>
            <when test="userName != null and userName != ''">
                AND user_name LIKE CONCAT('%', #{userName}, '%')
            </when>
            <when test="status != null">
                AND status = #{status}
            </when>
            <otherwise>
                AND age > 18 <!-- 默認條件:查詢 18 歲以上用户 -->
            </otherwise>
        </choose>
    </where>
</select>

foreach 標籤

遍歷 List、數組、Map 等集合類型參數,拼接成 IN 條件、批量插入的 VALUES 等 SQL 片段。

<foreach 
    collection="集合參數名"  <!-- 集合類型:List→list,數組→array,@Param→註解值 -->
    item="遍歷元素別名"      <!-- 遍歷集合時的單個元素名稱(如 user、id) -->
    index="索引/鍵名"       <!-- 可選:List→索引(0,1,2...),Map→key -->
    open="片段前綴"         <!-- 可選:遍歷結果的開頭(如 "IN (") -->
    close="片段後綴"        <!-- 可選:遍歷結果的結尾(如 ")") -->
    separator="元素分隔符"  <!-- 元素之間的分隔符(如 ",") -->
>
    #{item} <!-- 引用遍歷元素(如 #{id}、#{user.userName}) -->
</foreach>

<!-- 根據 ID 列表查詢用户 -->
<select id="selectUserByIds" resultType="User">
    SELECT id, user_name, age FROM user
    <where>
        id IN
        <foreach collection="idList" item="id" open="(" close=")" separator=",">
            #{id}
        </foreach>
    </where>
</select>

<!-- 批量插入用户 -->
<insert id="batchInsertUser">
    INSERT INTO user (user_name, age, status)
    VALUES
    <foreach collection="list" item="user" separator=",">
        (#{user.userName}, #{user.age}, #{user.status})
    </foreach>
</insert>

where 標籤

自動處理標籤內 SQL 片段的 AND/OR 前綴,無需手動寫 WHERE 1 = 1。

<select id="selectUserByCondition" resultType="User">
    SELECT id, user_name, age, status FROM user
    <where>
    	<if test="userName != null and userName != ''">
        	AND user_name LIKE CONCAT('%', #{userName}, '%')
    	</if>
    	<if test="age != null and age > 0">
        	AND age = #{age}
    	</if>
    </where>
</select>

set 標籤

動態拼接 UPDATE 語句的 SET 子句,自動處理字段後的逗號(避免多餘逗號導致語法錯誤)。

<update id="updateUser" parameterType="User">
    UPDATE user
    <set>
        <if test="userName != null and userName != ''">
            user_name = #{userName},
        </if>
        <if test="age != null">
            age = #{age},
        </if>
        <if test="status != null">
            status = #{status}
        </if>
    </set>
    WHERE id = #{id}
</update>

trim 標籤

trim 標籤有4個屬性:

  • prefix:給拼接後的 SQL 片段添加前綴
  • suffix:給拼接後的 SQL 片段添加後綴
  • prefixOverrides:刪除 SQL 片段開頭的指定字符,多個值用 | 分隔
  • suffixOverrides:刪除 SQL 片段結尾的指定字符,多個值用 | 分隔

trim 標籤會先拼接 <trim> 內部的 SQL 片段(如 <if> 標籤中的內容), 剔除片段開頭的 prefixOverrides 字符、結尾的 suffixOverrides 字符,給處理後的片段添加 prefix 前綴、suffix 後綴,若內部片段為空(所有 <if> 不生效),則 <trim> 整體不生成任何內容。

sql、include 標籤

<!-- 抽取公共查詢字段 -->
<sql id="userColumns">id, user_name, age, status</sql>

<!-- 引用公共片段 -->
<select id="selectUserById" resultType="User">
    SELECT <include refid="userColumns"/> FROM user WHERE id = #{id}
</select>