首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

MyBatis 動態 SQL 核心標籤深度解析:if、where、trim、choose、foreach 實戰指南

     編輯:Java 相關知識

動態 SQL 的核心價值

MyBatis 動態 SQL 基於 OGNL 表達式語言,在 XML 中通過標籤實現條件判斷、循環遍歷、片段拼接,將 Java 流程控制邏輯映射到 SQL 構建過程,實現「一條 SQL 應對多種查詢場景」。本文系統講解 5 大核心標籤的語法、屬性、常見陷阱與最佳實踐。

1. selectKey - 主鍵生成與回填

適用於數據庫不支持自增、或使用序列/自定義函數生成主鍵的場景。

<insert id="createStudentAutoKey" parameterType="StudentEntity" keyProperty="studentId">
  <selectKey keyProperty="studentId" resultType="String" order="BEFORE">
    select nextval('student_seq')  -- Oracle 序列 / 自定義函數
  </selectKey>
  INSERT INTO STUDENT_TBL(STUDENT_ID, STUDENT_NAME, ...) 
  VALUES (#{studentId}, #{studentName}, ...)
</insert>

關鍵屬性:

  • order="BEFORE":先查主鍵,再執行 INSERT(適用序列/函數)
  • order="AFTER":先執行 INSERT,再查主鍵(適用自增/identity)
  • statementType:STATEMENT/PREPARED/CALLABLE,默認 PREPARED

2. if - 條件判斷基石

基本語法

<select id="getStudentListIf" resultMap="studentResultMap" parameterType="StudentEntity">
  SELECT * FROM STUDENT_TBL ST
  <where>
    <if test="studentName != null and studentName != ''">
      AND ST.STUDENT_NAME LIKE CONCAT('%', #{studentName}, '%')
    </if>
    <if test="studentSex != null">
      AND ST.STUDENT_SEX = #{studentSex}
    </if>
    <if test="classId != null and classId != ''">
      AND ST.CLASS_ID = #{classId}
    </if>
  </where>
</select>

OGNL 表達式技巧

  • 屬性導航:classEntity.classId != null
  • 集合判空:ids != null and ids.size() > 0
  • 字符串非空:name != null and name != ''(推薦寫法)

3. where / trim - 智能 WHERE 子句處理

where 標籤

自動處理:

  1. 若內容非空,自動添加 WHERE 關鍵字
  2. 自動移除首個 AND/OR
<where>
  <if test="state != null"> state = #{state} </if>
  <if test="title != null"> AND title LIKE #{title} </if>
  <if test="author != null"> AND author_name LIKE #{author.name} </if>
</where>

trim 標籤(更靈活)

<!-- 等價於 where -->
<trim prefix="WHERE" prefixOverrides="AND |OR ">
  <if test="state != null"> state = #{state} </if>
  <if test="title != null"> AND title LIKE #{title} </if>
</trim>

<!-- 等價於 set (UPDATE 用) -->
<trim prefix="SET" suffixOverrides=",">
  <if test="name != null"> name = #{name}, </if>
  <if test="age != null"> age = #{age}, </if>
</trim>

屬性詳解:

  • prefix:前綴(如 WHERE、SET)
  • prefixOverrides:移除前綴後的關鍵字(區分大小寫,需包含空格)
  • suffix:後綴
  • suffixOverrides:移除末尾字符(如 UPDATE 的逗號)

4. choose / when / otherwise - 互斥條件選擇

類似 Java switch-case-default,只執行第一個匹配的 when。

<select id="getStudentListChoose" resultMap="studentResultMap" parameterType="StudentEntity">
  SELECT * FROM STUDENT_TBL ST
  <where>
    <choose>
      <when test="studentName != null and studentName != ''">
        ST.STUDENT_NAME LIKE CONCAT('%', #{studentName}, '%')
      </when>
      <when test="studentSex != null">
        AND ST.STUDENT_SEX = #{studentSex}
      </when>
      <when test="studentBirthday != null">
        AND ST.STUDENT_BIRTHDAY = #{studentBirthday}
      </when>
      <otherwise>
        1=1  -- 默認條件,防止 WHERE 後無內容
      </otherwise>
    </choose>
  </where>
</select>

對比 if

特性ifchoose/when
邏輯關係AND(全部匹配生效)OR(優先匹配一個生效)
適用場景多條件組合篩選互斥查詢方式(如:按ID/按名/按時間三選一)

5. foreach - 集合遍歷與 IN 查詢

核心屬性

屬性說明示例值
collection集合參數名(List->"list",Array->"array",Map->key名)"list" / "ids"
item迭代變量名"id" / "item"
index索引變量名(可選)"idx"
open開始字符"("
separator分隔符","
close結束字符")"

場景 1:數組參數

// 接口
List<Student> getByClassIdsArray(String[] classIds);


<select id="getByClassIdsArray" resultMap="studentResultMap">
  SELECT * FROM STUDENT_TBL WHERE CLASS_ID IN
  <foreach collection="array" item="cid" open="(" separator="," close=")">
    #{cid}
  </foreach>
</select>

場景 2:List 參數

// 接口
List<Student> getByClassIdsList(List<String> classIdList);


<foreach collection="list" item="cid" open="(" separator="," close=")">
  #{cid}
</foreach>

場景 3:Map 封裝參數

// 接口
List<Blog> dynamicForeachTest(Map<String, Object> params);


<select id="dynamicForeachTest" resultType="Blog">
  SELECT * FROM T_BLOG WHERE TITLE LIKE "%"#{title}"%" 
  AND ID IN
  <foreach collection="ids" item="item" open="(" separator="," close=")">
    #{item}
  </foreach>
</select>


Map<String, Object> params = new HashMap<>();
params.put("ids", Arrays.asList(1,2,3,6,7,9));
params.put("title", "中國");
mapper.dynamicForeachTest(params);

常見陷阱與避坑指南

陷阱現象解決方案
OGNL 中用 &&/||配置解析報錯必須用 and/or
where 標籤未生效SQL 語法錯誤確保 where 內容非空;或改用 trim
foreach collection 錯誤參數綁定失敗List用"list",Array用"array",Map用key名
${} 用於業務參數SQL 注入風險業務參數必用 #{}, 只在表名/列名/排序用 ${}
IN 列表超限(>1000)Oracle/MySQL 報錯分批查詢或用臨時表/JOIN

最佳實踐總結

  1. 優先用 where/trim:自動處理關鍵字,減少語法錯誤
  2. 互斥條件用 choose:邏輯清晰,避免多條件疊加
  3. 集合遍歷用 foreach:自動處理分隔符,防注入
  4. OGNL 表達式嚴謹判空:!= null and != '' 雙重檢查
  5. 提取公共 SQL 片段:<sql> + <include> 復用條件、排序、列定義
  6. 複雜動態 SQL 拆分:超過 50 行考慮拆分為多個映射或使用 Java 代碼構建

總結

MyBatis 動態 SQL 通過 if、choose、where/trim、foreach、selectKey 五大核心標籤,配合 OGNL 表達式,提供了強大而優雅的 SQL 動態構建能力。掌握各標籤的適用場景、屬性配置、常見陷阱,能讓你編寫出既靈活又安全、既簡潔又高效的映射文件,是 MyBatis 進階開發的核心技能。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved