MyBatis 中動態(tài) SQL 的作用及執(zhí)行原理解析
一句話總結(jié)
MyBatis 動態(tài) SQL 用一組 XML 標(biāo)簽(<if>、<choose>、<foreach>、<where>、<set> 等)在 Mapper 層按條件動態(tài)拼接 SQL,避免在 Java 代碼里手動拼接字符串,實現(xiàn)多條件查詢、批量操作和按需更新等復(fù)雜場景,提升可讀性與可維護性,并配合 #{} 保證安全。
一、動態(tài) SQL 的核心作用
1. 靈活的條件查詢(多條件拼接WHERE)
根據(jù)參數(shù)是否為空,按需拼接 WHERE 條件,避免大量 if 拼接 SQL 字符串,以及多余的 AND / OR。
<select id="findUser" resultType="User">
SELECT * FROM user
<where>
<if test="name != null">
AND name = #{name}
</if>
<if test="age != null">
AND age = #{age}
</if>
</where>
</select>name、age都為空時:只生成SELECT * FROM user- 只有
name不為空:生成SELECT * FROM user WHERE name = ?
面試點:動態(tài) SQL 最典型用途之一就是多條件查詢,根據(jù)參數(shù)是否為空動態(tài)增加
WHERE條件。
2. 減少重復(fù)代碼(替代多層 if-else 分支)
使用 <choose> / <when> / <otherwise> 做互斥分支選擇,避免同一個 SQL 寫多份、或者在 Java 里寫一堆 if-else。
<select id="findUserByType" resultType="User">
SELECT * FROM user
WHERE role =
<choose>
<when test="role == 'admin'">
'ADMIN'
</when>
<when test="role == 'user'">
'USER'
</when>
<otherwise>
'GUEST'
</otherwise>
</choose>
</select>- 優(yōu)勢:
- XML 中邏輯集中統(tǒng)一、互斥關(guān)系清晰;
- 減少重復(fù) SQL 片段,維護方便。
3. 動態(tài)批量操作(IN條件、批量插入 / 更新)
通過 <foreach> 遍歷集合,動態(tài)生成 IN (...) 或批量插入的 VALUES (...) 列表。
<select id="selectByIds" resultType="User">
SELECT * FROM user WHERE id IN
<foreach collection="ids"
item="id"
open="("
separator=","
close=")">
#{id}
</foreach>
</select>- 典型場景:批量查詢、批量更新、批量刪除等,減少數(shù)據(jù)庫往返次數(shù)、提升性能。
4. 配合參數(shù)綁定,降低 SQL 注入風(fēng)險
通過 #{} 占位符和 <bind> 動態(tài)生成參數(shù)值,防止將用戶輸入直接拼到 SQL 里。
<select id="findByNameLike" resultType="User">
<bind name="pattern" value="'%' + name + '%'" />
SELECT * FROM user
WHERE name LIKE #{pattern}
</select>- 即使參數(shù)中包含
' OR 1=1 --,也會被當(dāng)成普通字符串,而不是 SQL 片段執(zhí)行。 - 注意:真正防注入的關(guān)鍵在于
#{}+ 預(yù)編譯,動態(tài) SQL 只是避免你再去拼字符串。
5. 動態(tài)更新與字段控制(按需更新非空字段)
使用 <set> 標(biāo)簽只更新非空字段,并自動處理多余逗號。
<update id="updateUser">
UPDATE user
<set>
<if test="name != null">
name = #{name},
</if>
<if test="age != null">
age = #{age},
</if>
</set>
WHERE id = #{id}
</update>- 只會更新有值的字段,避免把字段更新成
NULL; <set>會自動去掉最后一個逗號,生成合法 SQL。
二、典型應(yīng)用場景(面試可以照著說)
| 場景 | 動態(tài) SQL 標(biāo)簽組合 | 效果 |
|---|---|---|
| 多條件查詢 | <if> + <where> | 按需生成 WHERE 子句,避免冗余條件 |
| 分頁 + 條件查詢 | <if> + <where> + 分頁插件 | 條件 + 分頁統(tǒng)一在一個 Mapper 中維護 |
| 批量插入 / 更新 | <foreach> | 單條 SQL 處理多條數(shù)據(jù),減少數(shù)據(jù)庫交互 |
| 多表關(guān)聯(lián)查詢 | <choose> 動態(tài)選表 / 關(guān)聯(lián) | 根據(jù)業(yè)務(wù)條件選擇不同的關(guān)聯(lián)方式或表 |
| 動態(tài)排序 | <if> 控制 ORDER BY | 支持前端傳入排序字段、排序方式,實現(xiàn)靈活排序 |
一句話記憶:動態(tài) SQL 常用在多條件查詢、批量操作、動態(tài)排序、部分字段更新、復(fù)雜關(guān)聯(lián)這幾類場景。
三、動態(tài) SQL 的執(zhí)行原理(加分項)
- OGNL 表達式解析
- MyBatis 使用 OGNL(Object-Graph Navigation Language)解析
<if test="...">、<foreach>等標(biāo)簽中的條件表達式; - 通過
test="name != null and name != ''"這類表達式判斷是否拼接對應(yīng)的 SQL 片段。
- MyBatis 使用 OGNL(Object-Graph Navigation Language)解析
SqlNode組合成SqlSource- 所有動態(tài)標(biāo)簽先被解析成一棵
SqlNode樹(比如IfSqlNode、WhereSqlNode等); - 運行時根據(jù)入?yún)⒃谶@棵樹上“走一遍”,把滿足條件的節(jié)點拼接成完整 SQL,生成
SqlSource。
- 所有動態(tài)標(biāo)簽先被解析成一棵
- 預(yù)編譯與參數(shù)綁定
- 最終 SQL 仍然通過
PreparedStatement預(yù)編譯; #{}會變成?占位符,參數(shù)通過 JDBC 安全綁定,既防注入又可復(fù)用執(zhí)行計劃。
- 最終 SQL 仍然通過
四、實際場景示例:多條件搜索用戶
<select id="searchUsers" resultType="User">
SELECT * FROM user
<where>
<if test="name != null and name != ''">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
<if test="minAge != null">
AND age >= #{minAge}
</if>
<if test="roles != null and roles.size() > 0">
AND role IN
<foreach collection="roles"
item="role"
open="("
close=")"
separator=",">
#{role}
</foreach>
</if>
</where>
ORDER BY create_time DESC
</select>- 支持:模糊查詢、年齡下限過濾、角色集合過濾;
- 傳哪個條件就拼哪個條件,未傳的不生效;
- 所有入?yún)⒍甲?
#{},安全、可預(yù)編譯。
五、面試回答模板(直接背)
- 問:MyBatis 動態(tài) SQL 有什么作用?常用在哪些場景?
- 答:“MyBatis 通過
<if>、<choose>、<foreach>、<where>、<set>等標(biāo)簽,在 XML 里按條件動態(tài)拼接 SQL,代替 Java 代碼里手動拼字符串。常用在多條件查詢、批量插入 / 更新、動態(tài)排序、按需更新部分字段、復(fù)雜關(guān)聯(lián)查詢等場景,既提高了可讀性和維護性,又能配合#{}和預(yù)編譯保證安全。”
- 答:“MyBatis 通過
到此這篇關(guān)于MyBatis 中動態(tài) SQL 的作用及執(zhí)行原理解析的文章就介紹到這了,更多相關(guān)mybatis動態(tài)sql使用內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot快速集成jxls-poi(自定義模板,支持本地文件導(dǎo)出,在線文件導(dǎo)出)
這篇文章主要介紹了SpringBoot快速集成jxls-poi(自定義模板,支持本地文件導(dǎo)出,在線文件導(dǎo)出),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09
Java使用OpenCV實現(xiàn)實時對象追蹤的代碼示例
視覺跟蹤是指通過分析視頻幀來自動追蹤一個或多個移動對象的過程,這項技術(shù)廣泛應(yīng)用于監(jiān)控、人機交互、增強現(xiàn)實和自動駕駛等領(lǐng)域,本文給大家介紹了Java如何使用OpenCV實現(xiàn)實時對象追蹤,需要的朋友可以參考下2025-09-09
spring boot實現(xiàn)profiles動態(tài)切換的示例
Spring Boot支持在不同的環(huán)境下使用不同的配置文件,該技術(shù)非常有利于持續(xù)集成,在構(gòu)建項目的時候只需要使用不同的構(gòu)建命令就可以生成不同運行環(huán)境下war包,而不需要手動切換配置文件。2020-10-10
Java設(shè)計模式之觀察者模式_動力節(jié)點Java學(xué)院整理
這篇文章給大家介紹流量java設(shè)計模式之觀察者模式,定義對象間一種一對多的依賴關(guān)系,使得當(dāng)每一個對象改變狀態(tài)。下面通過類圖和實例代碼給大家介紹java設(shè)計模式之觀察者模式,感興趣的朋友一起看看吧2017-08-08

