Mybatis05-動態sql

一、應用場景

MyBatis 的 動態 SQL 是指根據不同的條件動態拼接生成 SQL 語句的能力。它的最大優勢是:避免寫多個 XML 映射語句、避免 SQL 冗余、提升代碼復用性和可維護性

示例1:用戶可以通過勾選框,勾選不同的數據進行批量刪除,此時:

delete from xxx_table where id in (a, b, c, d, ......)

in后面括號中的數據就是動態的。

示例2:用戶搜索功能,查詢條件不確定(常用于搜索/篩選)

二、使用方式(XML + 標簽)

MyBatis 動態 SQL 主要通過 XML 配置文件中的標簽 來實現,核心標簽如下:

標簽作用
<if>條件判斷
<choose> / <when> / <otherwise>類似 Java 中的 if-else if-else
<where>自動加上 WHERE 并避免多余的 AND/OR
<set>用于 UPDATE,自動去除最后的逗號
<foreach>用于遍歷集合(常見于 IN、批量插入)
<trim>自定義前綴、后綴、去掉前后符號等

2-1、<if>標簽

示例:

    List<Car> selectByMutiConditions(@Param("brand") String brand,@Param("guidePrice") Double guidePrice,@Param("carType") String carType);

?

    <select id="selectByMutiConditions" resultType="car">select * from t_car where 1=1/*1. if標簽中,test屬性是必須的,值:表達式(false/true)2. test屬性可以使用的是:使用了@Param標簽,就用@Param標簽指定的參數名;沒有使用@Param標簽,就用:param1, param2, param3, arg0, arg1, arg2...當使用了pojo,就用pojo中的屬性名3. 在mybatis的動態sql中,不能使用&&,只能使用and*/<if test="brand != null and brand != ''">and brand like "%"#{brand}"%"</if><if test="guidePrice != null and guidePrice != ''">and guidePrice > #{guidePrice}</if><if test="carType != null and carType != ''">and carType = #{carType}</if></select>

【注意點】:

1. if標簽中,test屬性是必須的,值:表達式(false/true)

2. test屬性可以使用的是:

  • ??? 使用了@Param標簽,就用@Param標簽指定的參數名;
  • ??? 沒有使用@Param標簽,就用:param1, param2, param3, arg0, arg1, arg2...
  • ??? 當使用了pojo,就用pojo中的屬性名

3. 在mybatis的動態sql中,不能使用&&,只能使用and

4、為了防止有部分條件不成立的時候,where后面直接拼接了and,需要加上1=1

2-2、<where>標簽

它的作用是在生成 SQL 語句時:

  1. 自動去掉首個條件多余的 ANDOR

  2. 當有條件時自動加上 WHERE 關鍵字

  3. 當沒有任何條件時不會生成 WHERE 子句

【注意】:

????????<where> 標簽只對內容中出現的 第一個 AND/OR 進行清理,建議把每個 <if> 條件前都加上 AND,它會自動清理第一個多余的。

示例:

?

2-3、<trim>標簽

在 MyBatis 中,<trim> 是一個功能非常強大的 動態 SQL 標簽,它可以靈活地控制:

  • 前綴(prefix):開頭加什么

  • 后綴(suffix):結尾加什么

  • 前綴去除(prefixOverrides):去掉開頭多余的關鍵字,比如 AND / OR / ,

  • 后綴去除(suffixOverrides):去掉結尾多余的內容,比如 ,


你可以把 <trim> 看成 <where><set> 的“底層基礎版本”,功能更靈活。


1、常見屬性說明

屬性名說明
prefix給內容加上前綴,比如 "WHERE""SET"
suffix給內容加上后綴
prefixOverrides去除內容前面多余的前綴(如多余的 "AND""OR"
suffixOverrides去除內容最后多余的后綴(如多余的 ","

【注意】:

它們 都是在整體 trim 內容拼接完畢后一次性處理整個 SQL 片段的前綴和后綴!?


示例 1:動態 WHERE 條件,代替 <where>

<select id="selectUser" parameterType="User" resultType="User">SELECT * FROM user<trim prefix="WHERE" prefixOverrides="AND |OR "><if test="username != null"> AND username = #{username} </if><if test="gender != null"> AND gender = #{gender} </if></trim>
</select>

效果(如果 username != null 且 gender == null):

SELECT * FROM user WHERE username = 'Tom'

自動去掉多余的 AND,和 <where> 效果一樣,但更靈活(你也可以換成 OR)。


示例 2:動態更新字段,代替 <set>

<update id="updateUser" parameterType="User">UPDATE user<trim prefix="SET" suffixOverrides=","><if test="username != null"> username = #{username}, </if><if test="gender != null"> gender = #{gender}, </if></trim>WHERE id = #{id}
</update>

效果:

  • 如果 username = 'Tom', gender = null:

    UPDATE user SET username = 'Tom' WHERE id = 1
    

    自動去掉最后多余的逗號 ,


示例 3:完全自定義的 SQL 拼接

<trim prefix="(" suffix=")" suffixOverrides=","><foreach collection="ids" item="id">#{id},</foreach>
</trim>

輸出示例:

(1, 2, 3)

2-4、<set>標簽

<set> 標簽的作用是:

  • 自動添加 SET 關鍵字

  • 自動去除多余的結尾逗號 ,

  • <if> 標簽組合使用,實現根據條件動態更新字段

目的:希望更新的時候,只更新不為null的字段,其余字段不動!

?

2、基本語法結構

<update id="updateUser" parameterType="User">UPDATE user<set><if test="username != null"> username = #{username}, </if><if test="email != null"> email = #{email}, </if><if test="age != null"> age = #{age}, </if></set>WHERE id = #{id}
</update>

3、執行邏輯過程

假設只傳了 usernameageemail 是 null。

MyBatis 會生成這樣的 SQL:

UPDATE user
SET username = ?, age = ?
WHERE id = ?

注意:

  • ? 自動添加了 SET

  • ? 自動移除了末尾多余的 ,


4、和 <trim> 的關系

<set> 本質上就是 <trim> 的封裝版本

等價于:

<trim prefix="SET" suffixOverrides=",">...
</trim>

也就是說,如果你希望自定義更多行為(如不使用 SET、用別的關鍵字),可以改用 <trim> 標簽

2-5、<choose>、<when>、<otherwise>

MyBatis 中的 <choose> 標簽是用來實現 類似 Java 中 if-else if-else 結構的動態 SQL 分支選擇語句,非常適合你有多個條件分支,但只想執行其中一個的情況


1、 作用:

<choose> 是 MyBatis 提供的一個邏輯分支標簽,用來表示:

如果滿足第一個條件就執行它,否則判斷第二個條件……都不滿足則執行默認分支。

它的結構和 Java 中的 if ... else if ... else 是一致的:

if (a != null) {// ...
} else if (b != null) {// ...
} else {// default
}

在 MyBatis 中寫法如下:

<choose><when test="a != null">...</when><when test="b != null">...</when><otherwise>...</otherwise>
</choose>

2、使用示例

例子:根據傳入的條件查詢用戶

<select id="findUser" parameterType="map" resultType="User">SELECT * FROM user<where><choose><when test="id != null">id = #{id}</when><when test="username != null">username = #{username}</when><otherwise>gender = 'male'</otherwise></choose></where>
</select>

3、執行邏輯:

假設傳入參數是:

Map<String, Object> param = new HashMap<>();
param.put("username", "Tom");

生成的 SQL 就是:

SELECT * FROM user WHERE username = 'Tom'

如果傳入參數是:

param.put("id", 123);
param.put("username", "Tom");

優先滿足第一個條件:

SELECT * FROM user WHERE id = 123

如果兩個都沒傳,則使用 otherwise

SELECT * FROM user WHERE gender = 'male'

4、幾點注意事項

特性說明
<choose> 內只能選中 一個 <when> 被執行只執行第一個匹配成功的 <when>
<otherwise> 是可選的不寫也可以
多個 <when> 的順序很重要從上往下匹配,匹配到就停

2-6、<foreach>標簽

MyBatis 中的 <foreach> 標簽是動態 SQL 中非常重要的一個標簽,主要用于 遍歷集合(如 List、數組、Map)生成一段重復的 SQL 語句,特別適合用于:

批量插入、批量刪除、IN (...) 條件語句


1、 的核心作用

用來遍歷集合(如 List數組Map),在 SQL 中動態生成重復片段,比如:

  • id IN (1, 2, 3)

  • 多行 VALUES 插入

  • 多個字段拼接


2、常見屬性說明

屬性名說明
collection要遍歷的集合名,如 listarraymap
item遍歷過程中每一項的變量名
index當前項的下標可選
open開始拼接的前綴(如 (
close拼接結束的后綴(如 )
separator每項之間的分隔符(如 ,

3、經典使用場景

(1). IN (...) 查詢
<select id="selectByIds" parameterType="list" resultType="User">SELECT * FROM userWHERE id IN<foreach collection="list" item="id" open="(" separator="," close=")">#{id}</foreach>
</select>

傳入:

List<Integer> ids = Arrays.asList(1, 2, 3);

生成 SQL:

SELECT * FROM user WHERE id IN (1, 2, 3)

(2). 批量插入
<insert id="insertUsers" parameterType="list">INSERT INTO user (username, age)VALUES<foreach collection="list" item="u" separator=",">(#{u.username}, #{u.age})</foreach>
</insert>

【注意】:

此時,屬性:open,close都沒有寫!?

傳入:

List<User> users = List.of(new User("Tom", 20),new User("Jerry", 22)
);

生成 SQL:

INSERT INTO user (username, age)
VALUES ('Tom', 20), ('Jerry', 22)

(3). 遍歷 Map,實現動態 UPDATE 字段
<foreach collection="map" item="val" index="key">${key} = #{val}
</foreach>

示例:

    int updateForEach(@Param("updateFields") Map<String, Object> updateFields,@Param("id") Long id);

【注意】:

????????此時的key用的是${key},因為是數據庫字段,不能用#{key},否則拼接的數據庫字段會加上字符串!


4、常見坑點提示

問題說明
collection="list"如果參數是 List,要寫 "list"(不是寫變量名)
<foreach> 必須配合 open/close/separator否則 SQL 拼接可能出錯
item=#{} 中的 item 要和聲明保持一致否則無法解析參數
避免用 ${} 插值容易引發 SQL 注入問題

2-7、<include>標簽

MyBatis 中的 <include> 標簽用于 在 XML 映射文件中復用 SQL 片段,類似 Java 中的方法提取,可以提高 SQL 語句的復用性和可維護性。


1、使用場景

  • 多個 SQL 中有相同字段、相同條件、重復的列名等

  • 希望統一維護公共 SQL 內容,避免拷貝粘貼


2、基本語法

1. 定義可復用的 SQL 片段(使用 <sql> 標簽)

<sql id="baseColumnList">id, name, age, gender, email
</sql>

2. 引用 SQL 片段(使用 <include> 標簽)

<select id="selectAll" resultType="User">SELECT<include refid="baseColumnList" />FROM users
</select>

3、完整示例

<!-- 定義通用字段列表 -->
<sql id="baseColumnList">id, name, age, gender, email
</sql><!-- 使用 include 引用 -->
<select id="selectById" resultType="User">SELECT <include refid="baseColumnList" />FROM usersWHERE id = #{id}
</select>

4、refid 范圍說明

  • <sql id="...">id 必須在當前命名空間中唯一

  • 如果跨命名空間使用,需要使用 namespace.id 引用,例如:

<include refid="com.example.mapper.UserMapper.baseColumnList" />

?? 注意事項

  • 不支持 <sql> 中再嵌套 <sql><include>!!!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/91309.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/91309.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/91309.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

VSCODE 選中多行 需要同時按住alt鍵才可以

在 VS Code 中&#xff0c;如果你發現 必須按住 Alt 鍵才能選中多行&#xff08;即“列選擇”或“塊選擇”模式&#xff09;&#xff0c;而直接拖動鼠標無法多選&#xff0c;可能是由于以下原因導致的&#xff1a;1. 檢查是否啟用了“列選擇模式”VS Code 默認情況下&#xff1…

2025前端面試真題以及答案-不斷整理中,問題來源于牛客真題

一、 項目內存泄露react與vue的渲染機制有哪些不同react fiber架構vue2與3&#xff0c;為什么用proxy代替defineproperty性能優化有哪些三欄布局實現方式重排與重繪一個對話聊天框如何減少重排&#xff08;我回答的是絕對定位&#xff0c;將聊天框定位在下面&#xff0c;類似于…

雷軍的 IP 革命:人格化力量如何重塑商業規則|創客匠人

小米 YU7 發布會 3 分鐘售罄 20 萬臺的奇跡&#xff0c;撕開了一個時代真相&#xff1a;當商業競爭進入深水區&#xff0c;決定勝負的不再是產品參數&#xff0c;而是創始人 IP 的人格穿透力。雷軍僅憑個人影響力撬動數十億級交易&#xff0c;這絕非偶然&#xff0c;而是人格化…

SpringBoot3:應對C10K并發挑戰的優化指南

嘿&#xff0c;哥們&#xff01;還在為服務的并發量上不去而頭疼嗎&#xff1f;用戶量一上來&#xff0c;CPU、內存就告急&#xff0c;接口響應慢得像蝸牛&#xff1f;別慌&#xff0c;今天我們就來盤一盤&#xff0c;怎么用最新的Spring Boot 3&#xff0c;把服務性能調教到極…

響應式編程入門教程第三節:ReactiveCommand 與 UI 交互

響應式編程入門教程第一節&#xff1a;揭秘 UniRx 核心 - ReactiveProperty - 讓你的數據動起來&#xff01; 響應式編程入門教程第二節&#xff1a;構建 ObservableProperty&#xff1c;T&#xff1e; — 封裝 ReactiveProperty 的高級用法 響應式編程入門教程第三節&#x…

500+技術棧覆蓋:Web測試平臺TestComplete的對象識別技術解析

在用戶界面&#xff08;UI&#xff09;測試領域&#xff0c;傳統的測試工具往往依賴于XPath或CSS選擇器來定位頁面元素。然而&#xff0c;在面對動態變化的界面、多語言支持或是跨越多種技術框架的應用時&#xff0c;這些傳統方法常導致腳本失效&#xff0c;增加了維護成本。 …

研究人員利用提示注入漏洞繞過Meta的Llama防火墻防護

Trendyol應用安全團隊發現了一系列繞過技術&#xff0c;使得Meta的Llama防火墻在面對復雜的提示注入攻擊時防護失效。這一發現引發了人們對現有大語言模型&#xff08;LLM&#xff09;安全措施準備情況的擔憂&#xff0c;并凸顯出在企業日益將大語言模型嵌入工作流程時&#xf…

Shell 腳本系統學習 · 第5篇:多命令順序執行的三種方式詳解(`;`、``、`||`)

在日常的 Linux 運維與腳本編寫中&#xff0c;我們經常需要依次執行多條命令。本篇將帶你徹底搞懂三種命令順序執行方式&#xff1a;;、&& 和 ||&#xff0c;并通過實用示例掌握它們的區別與應用場景。一、為什么要了解多命令執行方式&#xff1f; 在實際運維或腳本編寫…

K8s存儲系統(通俗易懂版)

Kubernetes中存儲中有四個重要的概念&#xff1a;Volume、PersistentVolume PV、PersistentVolumeClaim PVC、StorageClass一、存儲系統核心概念Volume&#xff08;卷&#xff09;定義&#xff1a;Kubernetes 中最基礎的存儲單元&#xff0c;用于將外部存儲掛載到 Pod 中的容器…

小白學Python,標準庫篇——隨機庫、正則表達式庫

一、隨機庫1.隨機生成數值在random庫中可以隨機生成數值的方法有uniform()、random()、randint()、randrange()等。&#xff08;1&#xff09;uniform()方法uniform(參數1, 參數2)方法用于生成參數1到參數2之間的隨機小數&#xff0c;其中參數的類型都為數值類型。示例代碼&…

Qt窗口:菜單欄

目錄 一、窗口預覽 二、菜單欄 快捷鍵 子菜單 分割線 圖標 內存泄露 一、窗口預覽 在前面幾篇文章中&#xff0c;或者說&#xff0c;Qt初學階段&#xff0c;接觸到的都是QWidget&#xff0c;QWidget指控件&#xff0c;往往作為一個窗口的一部分出現。所謂的窗口&#x…

STM32裸機開發(中斷,輪詢,狀態機)與freeRTOS

裸機&#xff1a;沒有操作系統&#xff0c;程序是單流程的&#xff08;比如一個大循環里依次執行各個功能&#xff0c;或者用中斷嵌套處理事件&#xff09;。優點是資源占用極少&#xff08;幾乎不占 RAM/Flash&#xff09;、執行流程直觀&#xff1b;但復雜項目里&#xff0c;…

電腦上如何查看WiFi密碼

打開控制面板>點擊網絡和Internet在查看網絡和共享中心找到網絡狀態和任務點擊進去點擊連接的WLAN在WLAN狀態中點擊無線屬性在無線網絡屬性中點擊安全&#xff0c;點擊顯示字符&#xff08;H&#xff09;就可以顯示密碼了

文心一言4.5企業級部署實戰:多模態能力與Docker容器化測評

隨著大語言模型在企業服務中的應用日益廣泛&#xff0c;如何選擇一款既能滿足多模態創作需求&#xff0c;又具備良好企業級適配性的AI模型成為了關鍵問題。文心一言4.5作為百度最新開源的大模型&#xff0c;不僅在傳統的文本處理上表現出色&#xff0c;更是在多模態理解和企業級…

VUE Promise基礎語法

目錄 異步和同步 異步的問題 new Promise語法 promise的狀態 promise.then() Promise.resolve() Promise.reject() Promise.all() Promise.race() Promise.catch() Promise.finally() 異步和同步 同步模式下&#xff0c;代碼按順序執行&#xff0c;前一條執行完畢后…

用TensorFlow進行邏輯回歸(六)

import tensorflow as tfimport numpy as npfrom tensorflow.keras.datasets import mnistimport time# MNIST數據集參數num_classes 10 # 數字0到9, 10類num_features 784 # 28*28# 訓練參數learning_rate 0.01training_steps 1000batch_size 256display_step 50# 預處…

【HTTP版本演變】

在瀏覽器中輸入URL并按回車之后會發生什么1. 輸入URL并解析輸入URL后&#xff0c;瀏覽器會解析出協議、主機、端口、路徑等信息&#xff0c;并構造一個HTTP請求&#xff08;瀏覽器會根據請求頭判斷是否又HTTP緩存&#xff0c;并根據是否有緩存決定從服務器獲取資源還是使用緩存…

Android 16系統源碼_窗口動畫(一)窗口過渡動畫層級圖分析

一 窗口過渡動畫 1.1 案例效果圖1.2 案例源碼 1.2.1 添加權限 (AndroidManifest.xml) <!-- 系統懸浮窗權限&#xff08;Android 6.0需動態請求&#xff09; --> <uses-permission android:name"android.permission.SYSTEM_ALERT_WINDOW" />1.2.2 窗口顯示…

騰訊云WAF域名分級防護實戰筆記

基于業務風險等級、合規要求及騰訊云最佳實踐&#xff0c;提供可直接落地的配置方案&#xff0c;供學習借鑒&#xff1a;一、域名分級與防護原則1. ?域名分級清單&#xff08;核心資產&#xff09;???主域名??業務類型??風險等級??合規要求??防護等級?example.com…

1. 請說出你知道的水平垂直居中的方法

總結 容器 flex 布局&#xff0c;jsutify-content: center; align-items: center;容器 flex 布局&#xff0c;子項 margin: auto;容器 relative 布局&#xff0c;子項 absolute 布局&#xff0c;left: 50%; top: 50%; transform: translate(-50%, -50%);子項 absolute 布局&…