數據庫操作不再困難,MyBatis動態Sql標簽解析

系列文章目錄

MyBatis緩存原理
Mybatis的CachingExecutor與二級緩存
Mybatis plugin 的使用及原理
MyBatis四大組件Executor、StatementHandler、ParameterHandler、ResultSetHandler 詳解
MyBatis+Springboot 啟動到SQL執行全流程



在這里插入圖片描述

使用MyBatis,或者MyBatis-plus,有一項重要的開發技能就是寫動態sql,動態sql能幫我們省略很多復雜邏輯,也能幫我們解決一些格式問題,所以今天我們就來幫大家掌握這個動態sql

📕作者簡介:戰斧,從事金融IT行業,有著多年一線開發、架構經驗;愛好廣泛,樂于分享,致力于創作更多高質量內容
📗本文收錄于 MyBatis專欄 ,有需要者,可直接訂閱專欄實時獲取更新
📘高質量專欄 云原生、RabbitMQ、Spring全家桶 等仍在更新,歡迎指導
📙Zookeeper Redis kafka docker netty等諸多框架,以及架構與分布式專題即將上線,敬請期待


一、動態sql是什么?

假設有一個電商網站,需要根據用戶提供的不同條件查詢商品信息,比如說價格、商品名、類別等等,我們需要寫出每個查詢信息對應的SQL語句,例如
SELECT * FROM Product WHERE price <= #{price};
SELECT * FROM Product WHERE productname = #{name}
SELECT * FROM Product WHERE type = #{type};

如果這樣各種各樣的篩選邏輯很多,我們就需要寫很多條類似的SQL語句,而且還需要判斷入參是否合理,是否為空等等。這樣我們的sql就會非常的難以維護。此時,我們就需要利用到MyBatis的動態SQL

MyBatis的動態SQL是指在SQL語句中可以根據不同的條件動態生成不同的SQL語句,以適應不同的需求。動態SQL可以根據條件生成不同的SQL語句,比如WHERE條件、ORDER BY、動態判斷等等,從而實現更靈活的SQL編寫。動態SQL的主要作用是讓SQL語句更加靈活、可重用和易維護,提高應用程序的靈活性和可擴展性

二、動態sql原理

MyBatis動態sql標簽的實現原理主要是通過OGNL表達式和Java反射機制來實現的。

首先,MyBatis會將動態sql標簽中的OGNL表達式解析成Java代碼。解析過程中,MyBatis會根據OGNL表達式的類型來生成相應的Java代碼片段。例如,當解析if標簽時,如果判斷條件中的OGNL表達式為布爾類型,則會生成一個if語句的Java代碼片段。

其次,MyBatis會使用Java反射機制獲取實體類的屬性值,并將屬性值傳遞給動態sql標簽中的OGNL表達式進行判斷。如果判斷條件滿足,則會將動態sql標簽中的內容添加到SQL語句中;反之,則會忽略動態sql標簽中的內容

總體來講,MyBatis動態sql標簽的實現原理就是將OGNL表達式解析成Java代碼,并使用Java反射機制獲取實體類屬性值進行判斷,以實現SQL語句的動態生成。myBtais整體運行流程可以看往期的 MyBatis+Springboot 啟動到SQL執行全流程

三、動態標簽解釋

1. if 標簽

<if>標簽允許你根據條件判斷是否包含特定的SQL片段,用于構建靈活的查詢語句,該標簽的使用方法如下:

  1. 在需要動態生成條件的SQL語句中使用<if>標簽。
  2. <if>標簽中添加一個test屬性,用于指定一個boolean表達式,這個表達式會決定是否將當前條件語句包含在生成的SQL語句中。
  3. <if></if>標簽中設置需要動態生成的條件語句。
<select id="getUser" resultMap="userResultMap">SELECT * FROM userwhere 1 = 1<if test="name != null">AND name = #{name}</if><if test="age != null">AND age = #{age}</if>
</select>

在這個示例中,<if>標簽用于根據條件動態生成SQL語句中的條件語句。如果傳遞到這個方法的參數中包含一個非空的name屬性,那么就會在SQL語句中添加一個“name = #{name}”的條件語句;如果包含一個非空的age屬性,那么就會在SQL語句中添加一個“age = #{age}”的條件語句。

需要注意的是,使用標簽時,必須要用“AND”或“OR”將多個<if>標簽組合在一起,否則會出現語法錯誤。此外,還需要注意在使用標簽時,SQL語句的正確性和性能的折中。太多的動態條件可能會導致SQL語句生成的復雜度增加,從而影響查詢的性能

2 choose、when 和 otherwise 標簽

<choose><when><otherwise>標簽通常一起使用來實現條件分支查詢。這種方式類似于Java中的switch語句或者if-else語句

  • 其中,<choose>標簽類似于Java中的switch語句,表示一個選擇分支,包含多個<when><otherwise>標簽。
  • <when>標簽類似于Java中的case語句,用于指定分支條件。當條件滿足時,執行<when>標簽內的SQL語句。
  • <otherwise>標簽類似于Java中的default語句,用于指定默認條件分支。當其他分支條件都不滿足時,執行<otherwise>標簽內的SQL語句。
<select id="getUsersByAgeAndName" parameterType="map" resultMap="userMap">SELECT * FROM users<where><choose><when test="age != null and name != null">age = #{age} AND name = #{name}</when><when test="age != null">age = #{age}</when><when test="name != null">name = #{name}</when><otherwise>1=1</otherwise></choose></where>
</select>

如上,我們使用<choose><when><otherwise>標簽來實現條件分支查詢。這個查詢可以根據參數中的年齡和名字來查找用戶記錄,也可以只根據年齡或者名字來查找,或者不加任何條件查詢所有的用戶記錄

3 trim 標簽

<trim>標簽用于修剪生成的SQL語句,可以用于移除不必要的關鍵字,特別是在拼接多個條件時非常有用,其主要是用于確定子句中的前綴與后綴。

<trim> 標簽具有以下屬性:
prefixOverrides:要刪除的前綴字符串,可以以“|”分割,添加多個詞
suffixOverrides:要刪除的后綴字符串,可以以“|”分割,添加多個詞
prefix:要添加到 SQL 語句的前綴字符串
suffix:要添加到 SQL 語句的后綴字符串

<select id="findUserByName" parameterType="java.lang.String" resultType="User">SELECT * FROM user<trim prefix="WHERE" prefixOverrides="OR | AND "><if test="name != null and name != ''">AND name = #{name}</if><if test="age != null">AND age = #{age}</if></trim>
</select>

在這個示例中, <trim> 標簽用于動態構建 WHERE 子句。如果它會刪除前綴中的 OR 或 AND 關鍵字,并將 WHERE 關鍵字添加到 SQL 語句中。

當然,你也許會想,如果我把 prefixOverrides 和 prefix 是設置成一個詞,那這個詞還會不會插入到子句句首呢?答案是會的,因為這里的邏輯是先刪除prefixOverrides指定字符串,再添加prefix指定字符串

4 foreach 標簽

標簽用于循環處理集合類型的參數,生成多次重復的SQL片段,該標簽可以用于循環遍歷一個集合或數組,并在SQL語句中使用它的值。以下是標簽的使用方法:。

遍歷集合

<select id="findUserById" parameterType="java.lang.Integer">SELECT * FROM table WHERE id IN<foreach collection="list" item="item" open="(" separator="," close=")">#{item}</foreach>
</select>

其中,collection屬性指定要遍歷的集合,item屬性指定集合中每個元素的別名,open屬性指定在開始時添加的字符串,separator屬性指定在每個元素之間添加的字符串,close屬性指定在結束時添加的字符串。

例如,如果list是一個包含1、2、3的List對象,這個標簽會生成以下SQL語句:

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

遍歷數組

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

這個標簽與遍歷集合的標簽非常相似,只不過把collection屬性改為了數組對象

遍歷Map

假設我們有這樣一個map

Map<String, Object> searchCriteria = new HashMap<>();
searchCriteria.put("username", "john");
searchCriteria.put("age", 25);
searchCriteria.put("city", "New York");

然后我們配上這樣的xml

<select id="searchUsers" parameterType="map" resultType="User">SELECT * FROM user<foreach collection="searchCriteria" item="value" index="key" separator="AND" open="WHERE">${key} = #{value}</foreach>
</select>

其中,collection屬性指定要遍歷的Map集合,index屬性指定鍵的別名,item屬性指定值的別名,open屬性指定在開始時添加的字符串,separator屬性指定在每個元素之間添加的字符串。

最后我們能得到這樣的sql

SELECT * FROM user WHERE username = "john" AND age = 25 AND city = "New York"

5 set 標簽

標簽通常用于動態生成UPDATE語句的SET部分,可自動生成逗號分隔的鍵值對,<set>標簽通常包含多個標簽或者標簽,用于動態生成要更新的列和值。

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

在這個示例中,標簽根據傳入的參數動態生成SET部分。set 元素會動態地在行首插入 SET 關鍵字,并會刪掉額外的逗號

6 bind 標簽

<bind>用于將一個表達式綁定到一個變量上。綁定的變量可以在后面的SQL語句中引用。通常,標簽被用于簡化SQL語句中的重復表達式,提高SQL語句的可讀性和可維護性。

<select id="findUsers" parameterType="String" resultMap="userResultMap"><bind name="pattern" value="'%' + name + '%'"/>SELECT * FROM user WHERE name LIKE #{pattern}
</select>

在上面的例子中,標簽用于將一個字符串表達式綁定到名為pattern的變量上。變量的值是一個由‘%’,‘name’和‘%’組成的字符串,其中‘name’是一個參數。在后面的SQL語句中,#{pattern}即可引用綁定的變量。

在標簽中,name屬性指定變量的名稱,value屬性指定變量的值。可以在value屬性中使用表達式,變量名和參數Map中的鍵。

除了綁定表達式到變量上,標簽還可以用于綁定參數值到變量上。例如:

<select id="findUsersByAge" parameterType="int" resultMap="userResultMap"><bind name="minAge" value="age - 5"/><bind name="maxAge" value="age + 5"/>SELECT * FROM user WHERE age BETWEEN #{minAge} AND #{maxAge}
</select>

在上面的例子中,<bind>標簽用于將一個整數參數值綁定到名為minAge和maxAge的變量上。變量的值是通過計算參數值得到的。在后面的SQL語句中,#{minAge}和#{maxAge}即可引用綁定的變量。

總之,<bind>標簽是一個非常強大的MyBatis功能,可以提高SQL語句的可讀性和可維護性。在開發MyBatis應用程序時,應該靈活使用標簽,以便更好地管理SQL語句。

7 sql 與 include 標簽

<sql>標簽用于定義可重用的SQL片段。在一個或多個SQL語句中,可以使用標簽來引用這些SQL片段。


<sql id="userColumns">id, username, age
</sql><select id="getAllUsers" resultType="User">SELECT <include refid="userColumns" />FROM user
</select><select id="getUsersByName" parameterType="String" resultType="User">SELECT <include refid="userColumns" />FROM userWHERENAME = #{name}
</select>

上述例子中,<include>標簽引用了一個名為"userColumns"的SQL片段。引用的方式是通過refid屬性指定片段的ID。所有引用的SQL片段都會被添加到原始SQL語句中。這樣,其他sql,如果也是使用這幾個字段,則都可以引用

8. where 標簽

<where> 標簽的作用是用來包裹一個或多個 SQL 條件語句,如果這些條件都滿足,則會被加入到 SELECT 或 UPDATE 語句中,否則不會。它的使用方法如下:

1.將多個條件語句用 標簽包裹起來。

<select id="selectByCondition" parameterType="java.util.Map" resultMap="userMap">select * from user<where><if test="name != null and name!=''">and name like CONCAT('%',#{name},'%')</if><if test="age != null">and age = #{age}</if></where>
</select>

2.在 SQL 語句中使用 <where> 標簽時,會自動移除掉多余的 AND 或 OR,而在沒有任何條件語句時,<where> 標簽也會自動刪除自身,以避免 SQL 語法錯誤。例如,上述代碼在沒有任何條件時,生成的 SQL 語句為:

select * from user

而當輸入 age=20 時,生成的 SQL 語句為:

select * from userwhere age = 20

可以看出<where> 標簽是 MyBatis 中一個非常有用的標簽,它可以幫助我們更加方便地動態構建 SQL 語句,提高代碼的可讀性和可維護性。


總結

MyBatis的動態SQL功能極大地簡化了數據庫操作,使得針對不同查詢需求的SQL語句生成變得靈活而高效。通過深入了解和掌握<if>、<choose>、<when>、<otherwise>、<trim>、<foreach>、<set>、<bind>和<sql>等核心標簽,開發人員將大幅度減少因為參數不同,而寫不同sql的麻煩,此時的sql會自己按預設邏輯變成不同的樣式。因此,需要牢牢掌握。

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

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

相關文章

Neo4j之MATCH基礎

1】基本匹配和返回&#xff1a;查找所有節點和關系&#xff0c;返回節點的標簽和屬性。 MATCH (n) RETURN n;2】條件篩選&#xff1a;查找所有名為 "Alice" 的人物節點。 MATCH (person:Person {name: Alice}) RETURN person;3】關系查詢&#xff1a;查找所有和 &q…

Centos7.6 安裝mysql過程全記錄

在centos 7.6上 離線安裝mysql 的步驟&#xff0c;可參考下文&#xff1a; 一、查看當前MySQL的安裝情況并卸載 1. 查看當前MySQL的安裝情況 查找之前是否安裝了MySQL rpm -qa|grep -i mysql 2.卸載mysql 如果已經安裝mysql&#xff0c;則需要先停止MySQL&#xff0c;再刪除…

YOLOv5、YOLOv8改進:MobileViT:輕量通用且適合移動端的視覺Transformer

MobileViT: Light-weight, General-purpose, and Mobile-friendly Vision Transformer 論文&#xff1a;https://arxiv.org/abs/2110.02178 1簡介 MobileviT是一個用于移動設備的輕量級通用可視化Transformer&#xff0c;據作者介紹&#xff0c;這是第一次基于輕量級CNN網絡性…

LeetCode150道面試經典題--單詞規律(簡單)

1.題目 給定一種規律 pattern 和一個字符串 s &#xff0c;判斷 s 是否遵循相同的規律。 這里的 遵循 指完全匹配&#xff0c;例如&#xff0c; pattern 里的每個字母和字符串 s 中的每個非空單詞之間存在著雙向連接的對應規律。 2.示例 pattern"abba" s "c…

SpingBoot-Vue前后端——實現CRUD

目錄??????? 一、實例需求 ? 二、代碼實現 &#x1f3cc; 數據庫 &#x1f440; 后端實現 &#x1f4eb; 前端實現 &#x1f331; 三、源碼下載 &#x1f44b; 一、實例需求 ? 實現一個簡單的CRUD&#xff0c;包含前后端交互。 二、代碼實現 &#x1f3cc; 數…

[樹莓派]ImportError: libcblas.so.3: cannot open shared object file

嘗試在樹莓派4b安裝opencv-python,出現以下錯誤,ImportError: libcblas.so.3: cannot open shared object file: No such file or directory 解決方法&#xff0c;安裝依賴 sudo apt install libatlas-base-dev 再次import cv2就不會報這個錯誤。

約束綜合中的邏輯互斥時鐘(Logically Exclusive Clocks)

注&#xff1a;本文翻譯自Constraining Logically Exclusive Clocks in Synthesis 邏輯互斥時鐘的定義 邏輯互斥時鐘是指設計中活躍&#xff08;activate&#xff09;但不彼此影響的時鐘。常見的情況是&#xff0c;兩個時鐘作為一個多路選擇器的輸入&#xff0c;并根據sel信號…

八、解析應用程序——分析應用程序(1)

文章目錄 一、確定用戶輸入入口點1.1 URL文件路徑1.2 請求參數1.3 HTTP消息頭1.4 帶外通道 二、確定服務端技術2.1 提取版本信息2.2 HTTP指紋識別2.3 文件拓展名2.4 目錄名稱2.5 會話令牌2.6 第三方代碼組件 小結 枚舉盡可能多的應用程序內容只是解析過程的一個方面。分析應用程…

小龜帶你敲排序之冒泡排序

冒泡排序 一. 定義二.題目三. 思路分析&#xff08;圖文結合&#xff09;四. 代碼演示 一. 定義 冒泡排序&#xff08;Bubble Sort&#xff0c;臺灣譯為&#xff1a;泡沫排序或氣泡排序&#xff09;是一種簡單的排序算法。它重復地走訪過要排序的數列&#xff0c;一次比較兩個元…

【深度學習】再談向量化

前言 向量化是一種思想&#xff0c;不僅體現在可以將任意實體用向量來表示&#xff0c;更為突出的表現了人工智能的發展脈絡。向量的演進過程其實都是人工智能向前發展的時代縮影。 1.為什么人工智能需要向量化 電腦如何理解一門語言&#xff1f;電腦的底層是二進制也就是0和1&…

Arduino+esp32學習筆記

學習目標&#xff1a; 使用Arduino配置好藍牙或者wifi模塊 學習使用python配置好藍牙或者wifi模塊 學習內容&#xff08;筆記&#xff09;&#xff1a; 一、 Arduino語法基礎 Arduino語法是基于C的語法,C又是c基礎上增加了面向對象思想等進階語言。那就只記錄沒見過的。 單多…

全國各城市-貨物進出口總額和利用外資-外商直接投資額實際使用額(1999-2020年)

最新數據顯示&#xff0c;全國各城市外商直接投資額實際使用額在過去一年中呈現了穩步增長的趨勢。這一數據為研究者提供了對中國外商投資活動的全面了解&#xff0c;并對未來投資趨勢和政策制定提供了重要參考。 首先&#xff0c;這一數據反映了中國各城市作為外商投資的熱門目…

Effective Java筆記(31)利用有限制通配符來提升 API 的靈活性

參數化類型是不變的&#xff08; invariant &#xff09; 。 換句話說&#xff0c;對于任何兩個截然不同的類型 Typel 和 Type2 而言&#xff0c; List<Type1 &#xff1e;既不是 List<Type 2 &#xff1e; 的子類型&#xff0c;也不是它的超類型 。雖然 L ist<String…

Oracle自定義函數生成MySQL表結構的DDL語句

1. 自定義函數fnc_table_to_mysql create or replace function fnc_table_to_mysql ( i_owner in string, i_table_name in string, i_number_default_type in string : decimal, i_auto_incretment_column_name in stri…

Linux 文件查看命令

一、cat命令 1.cat文件名&#xff0c;查看文件內容&#xff1a; 例如&#xff0c;查看main.c文件的內容&#xff1a; 2.cat < 文件名&#xff0c;往文件中寫入數據&#xff0c; Ctrld是結束輸入 例如&#xff0c;向文件a.txt中寫入數據&#xff1a; 查看剛剛寫入a.txt的…

Yolov5(一)VOC劃分數據集、VOC轉YOLO數據集

代碼使用方法注意修改一下路徑、驗證集比例、類別名稱&#xff0c;其他均不需要改動&#xff0c;自動劃分訓練集、驗證集、建好全部文件夾、一鍵自動生成Yolo格式數據集在當前目錄下&#xff0c;大家可以直接修改相應的配置文件進行訓練。 目錄 使用方法&#xff1a; 全部代碼…

解決監督學習,深度學習報錯:AttributeError: ‘xxx‘ object has no attribute ‘module‘!!!!

哈嘍小伙伴們大家好呀&#xff0c;很長時間沒有更新啦&#xff0c;最近在研究一個問題&#xff0c;就是AttributeError: xxx object has no attribute module 今天終于是解決了&#xff0c;所以來記錄分享一下&#xff1a; 我這里出現的問題是&#xff1a; 因為我的數據比較大…

SQL優化

一、插入數據 優化 1.1 普通插入&#xff08;小數據量&#xff09; 普通插入&#xff08;小數據量&#xff09;&#xff1a; 采用批量插入&#xff08;一次插入的數據不建議超過1000條&#xff09;手動提交事務主鍵順序插入 1.2 大批量數據插入 大批量插入&#xff1a;&…

Android 開發中需要了解的 Gradle 知識

作者&#xff1a;wkxjc Gradle 是一個基于 Groovy 的構建工具&#xff0c;用于構建 Android 應用程序。在 Android 開發中&#xff0c;了解 Gradle 是非常重要的&#xff0c;因為它是 Android Studio 默認的構建工具&#xff0c;可以幫助我們管理依賴項、構建應用程序、運行測試…

macOS 如何安裝git和nvm

首先&#xff1a;先來安裝git 打開macOS終端 將下面的命令復制粘貼進去&#xff1a; curl -O https://mirrors.edge.kernel.org/pub/software/scm/git/git-2.41.0.tar.gz 版本號可以參考一下官網的 我這里安裝的是目前最新的2.41.0 然后在終端輸入下面的代碼或者雙擊git的…