動態(tài)SQL—SSM框架的學習與應用(Spring + Spring MVC + MyBatis)-Java EE企業(yè)級應用開發(fā)學習記錄(第三天)Mybatis的動態(tài)SQL操作
昨天我們深入學習了Mybatis的核心對象SqlSessionFactoryBuilder,掌握MyBatis核心配置文件以及元素的使用,也掌握MyBatis映射文件及其元素的使用。那么今天我們需要掌握的是更加復雜的查詢操作。
- 學會編寫MyBatis中動態(tài)SQL
- 學會MyBatis的條件查詢操作
- 學會MyBatis的更新操作
- 學會MyBatis的復雜查詢操作
一、什么是動態(tài)SQL?
? MyBatis 中的動態(tài) SQL 是一種在 SQL 查詢語句中根據(jù)不同的條件來動態(tài)生成不同的 SQL 片段的技術。它允許你根據(jù)不同的情況來構建查詢條件,避免寫大量重復的 SQL 語句。
動態(tài)SQL常用元素
元素 | 說明 |
---|---|
< if > | 判斷語句,用于單條件判斷 |
< choose >(< when >、< otherwise >) | 相當于Java中的switch…case…default語句,用于多條件判斷 |
< where > | 簡化SQL語句中where的條件判斷 |
< trim > | 可以靈活地去除多余的關鍵字 |
< set > | 用于SQL語句的動態(tài)更新 |
< foreach > | 循環(huán)語句,常用于in語句等列舉條件中 |
動態(tài) SQL 通常用于構建復雜的查詢條件,例如:
- 根據(jù)不同的條件組合進行查詢,比如根據(jù)姓名、性別、年齡等多個條件進行篩選。
- 根據(jù)不同的條件來動態(tài)排序查詢結果。
- 在更新操作中,根據(jù)傳入的參數(shù)來選擇性地更新某些字段。
- 在插入操作中,根據(jù)不同的參數(shù)情況來選擇性地插入某些字段。
MyBatis 提供了以下方式來實現(xiàn)動態(tài) SQL:
-
使用
<if>
元素: 可以根據(jù)條件來動態(tài)生成 SQL 片段。例如:<select id="findByCondition" parameterType="map" resultType="User"> SELECT * FROM users <where> <if test="name != null"> AND name = #{name} </if> <if test="age != null"> AND age = #{age} </if> </where> </select>
代碼解釋如下:
<select>
標簽定義了一個查詢語句,并指定了它的唯一標識符(id)、輸入?yún)?shù)類型(parameterType)和結果類型(resultType)。SELECT * FROM users
是實際的 SQL 查詢語句,表示從名為 “users” 的表中選取所有列的數(shù)據(jù)。<where>
標簽是 MyBatis 提供的一個用于構建動態(tài) SQL 的元素,它會在生成的 SQL 語句中添加 “WHERE” 關鍵字,并且只會在至少有一個條件滿足時添加。<if>
標簽是<where>
標簽中的子元素,用于判斷某個條件是否成立。它的 “test” 屬性用于指定一個條件表達式。如果
test="name != null"
成立,那么會在生成的 SQL 語句中添加一個 “AND name = #{name}” 的條件。如果
test="age != null"
成立,那么會在生成的 SQL 語句中添加一個 “AND age = #{age}” 的條件。這樣,在實際執(zhí)行查詢時,如果傳入的參數(shù)中有 “name”,那么會根據(jù) “name” 的值添加相應的查詢條件;同樣地,如果傳入的參數(shù)中有 “age”,那么會根據(jù) “age” 的值添加相應的查詢條件。
-
使用
<choose>
元素: 類似于 switch 語句,根據(jù)條件選擇一個分支進行處理。<select id="findByCondition" parameterType="map" resultType="User"> SELECT * FROM users <where> <choose> <when test="name != null"> AND name = #{name} </when> <when test="age != null"> AND age = #{age} </when> <otherwise> AND 1=1 </otherwise> </choose> </where> </select>
代碼解釋:
這段代碼使用了 MyBatis 中的動態(tài) SQL 元素
<choose>
元素,也稱為<choose>/<when>/<otherwise>
構造。這樣的構造允許你在 SQL 查詢中根據(jù)條件動態(tài)地生成不同的部分。<choose>
包含了多個<when>
元素,每個<when>
元素對應一個條件判斷,如果條件滿足,就會生成相應的 SQL 查詢條件。如果所有條件都不滿足,就會執(zhí)行<otherwise>
部分。簡單來說,這個查詢語句的意思是:
- 如果傳入的參數(shù)
name
不為null
,則添加條件AND name = #{name}
到查詢語句中。 - 如果傳入的參數(shù)
age
不為null
,則添加條件AND age = #{age}
到查詢語句中。 - 如果以上條件都不滿足(即都為
null
),則添加條件AND 1=1
到查詢語句中。這個條件相當于一個恒為真的條件,因此不會影響查詢結果。
- 如果傳入的參數(shù)
-
使用
<trim>
元素: 可以根據(jù)條件來剪切 SQL 片段的開始或結尾,以防止出現(xiàn)多余的關鍵字。<select id="findByCondition" parameterType="map" resultType="User"> SELECT * FROM users <where> <trim prefix="AND (" suffix=")" prefixOverrides="OR"> <if test="name != null"> OR name = #{name} </if> <if test="age != null"> OR age = #{age} </if> </trim> </where> </select>
上述代碼使用了 MyBatis 的動態(tài) SQL 元素中的
<trim>
元素,用于構建更復雜的條件語句。<trim>
元素用于構建一個以AND
為前綴和)
為后綴的條件片段,同時在生成條件語句時去除可能多余的OR
。簡單來說,這個查詢語句的意思是:
- 如果傳入的參數(shù)
name
不為null
,則將OR name = #{name}
添加到條件片段中。 - 如果傳入的參數(shù)
age
不為null
,則將OR age = #{age}
添加到條件片段中。
<trim>
元素的prefix
屬性指定前綴,suffix
屬性指定后綴,prefixOverrides
屬性指定需要去除的前綴。在這個例子中,prefix
是AND (
,suffix
是)
,prefixOverrides
是OR
。這樣,如果有至少一個條件滿足,就會生成類似AND ( condition1 OR condition2 )
的查詢語句。如果沒有條件滿足,就會生成AND ()
,這不會影響查詢結果。 - 如果傳入的參數(shù)
-
使用
<foreach>
元素: 可以用來遍歷集合或數(shù)組參數(shù),生成多個相同的 SQL 片段。<select id="findByIds" parameterType="list" resultType="User"> SELECT * FROM users WHERE id IN <foreach item="id" collection="list" open="(" separator="," close=")"> #{id} </foreach> </select>
這段代碼是一個 MyBatis 的動態(tài) SQL 查詢語句,用于根據(jù)傳入的一組 id 值查詢對應的用戶信息。這個查詢語句利用了 <foreach>
元素,允許在 SQL 查詢中動態(tài)地生成 IN
子句。
具體來說,這個查詢語句的意思是:
- 查詢
users
表中的所有列(SELECT *
)。 - 使用
WHERE
子句進行過濾,條件是id
值在傳入的list
參數(shù)中。 -
<foreach>
元素用于循環(huán)遍歷傳入的list
參數(shù),將每個id
值包裝在(
和)
之間,并使用,
作為分隔符。
在這個查詢語句中,<foreach>
元素的屬性含義如下:
-
item
:指定在每次循環(huán)中使用的變量名,這里是id
。 -
collection
:指定要遍歷的集合參數(shù)的名字,這里是list
。 -
open
:指定循環(huán)的開始字符,這里是(
。 -
separator
:指定循環(huán)每個元素之間的分隔符,這里是,
。 -
close
:指定循環(huán)的結束字符,這里是)
。
這樣,如果傳入一個列表參數(shù) list
,比如 [1, 2, 3]
,就會生成類似 SELECT * FROM users WHERE id IN (1, 2, 3)
的查詢語句,從而查詢對應的用戶信息。
這些動態(tài) SQL 的特性使得 MyBatis 在構建靈活和動態(tài)的查詢條件時非常方便,避免了硬編碼大量重復的 SQL 語句。
二、MyBatis的條件查詢操作
? 在學習了上面的幾個動態(tài)SQL的常用元素,現(xiàn)在我們來嘗試一下編寫一下我們自己項目的動態(tài)SQL。
1.編寫一個使用<if>
的動態(tài)SQl查詢語句,若有賬號名稱Account則按賬號名稱Account查詢,若有網(wǎng)站名WebsiteName則用WebsiteName查詢,若倆個都有則倆個都匹配的才查詢。
PasswordMSMapper.xml中編寫動態(tài)sql語句:
①首先進行單條件查詢測試,若Account不為空:
②也是單條件查詢測試,若WebsiteName不為空:
③若是倆個條件都滿足呢?Account和WebsiteName都不為空:
④若是倆個條件都不滿足呢?Account和WebsiteName都為空:
? 可以看到Mybatis是會根據(jù)我們輸入的查詢條件進行拼接生成動態(tài)的SQL。
2.那么我們繼續(xù)嘗試編寫<choose>
的動態(tài)sql語句,情景時:若有賬號名Account,則優(yōu)先使用賬號名進行查詢。若無賬戶名,有網(wǎng)站名WebsiteName,那么就按網(wǎng)站名搜索。若賬號名,網(wǎng)站名都無,那么就采用默認查詢passwordms表中的所有信息。
? 在使用<if>
元素時,只要test屬性中的表達式為true,就會執(zhí)行元素中的條件語句,但是在實際應用中,有時只需要從多個選項中選擇一個去執(zhí)行。這時候使用<choose>
就是非常合適的,如果使用<if>
則是不合適的。
PasswordMSMapper.xml中編寫動態(tài)sql語句:
那么去編寫我們的測試類,根據(jù)不同的輸入情況,查看是否滿足我們的需求?
①Account不為空,WebsiteName為空的情況:
②Account不為空,WebsiteName不為空的情況(結果和上面的一樣):
③Account為空,WebsiteName不為空的情況:
④Account為空,WebsiteName也為空的情況:
? 可以看到Mybatis是會根據(jù)我們輸入的查詢條件進行拼接生成動態(tài)的SQL。
3.那么我們繼續(xù)嘗試編寫<where>
、<trim>
的動態(tài)sql語句,情景時:在sql中where 后面直接跟and 或者 or之類的關鍵字都是錯誤的,會導致我們的SQL語句運行不成功
在這個示例中我們寫了個<trim> prefix="AND (" suffix=")" prefixOverride="AND"
,
在<if>
條件中Account滿足時,程序會自動在AND Account=#{Account}前面、后面,添加上前后綴生成新的語句:
AND (AND Account=#{Account}),
若是WebsiteName也滿足的話,新的sql語句為:
AND (AND Account=#{Account} AND WebsiteName = #{WebsiteName} )
我們寫的prefixOverrides=“AND”,會自動將()里面的多余的前綴AND去除,即生成:
AND ( Account=#{Account} AND WebsiteName = #{WebsiteName} )
看到這里是不是覺得那外面還有一個where呢?where AND (Account=#{Account} AND WebsiteName = #{WebsiteName})
也是錯的對不?實際上<where>
會自動幫我們?nèi)コ?)外多余的AND和OR防止SQL語句出錯。
因此這個流程下來我們的語句就變成了:
where ( Account=#{Account} AND WebsiteName = #{WebsiteName} )
我們?nèi)ポ斎霚y試類看看就知道是不是了
①Account不為空,WebsiteName也不為空:
②Account為空,WebsiteName不為空:
根據(jù)我們上面講的流程:那么推測生成的動態(tài)sql語句應該是:
where ( WebsiteName=#{WebsiteName} )
③Account、WebsiteName都為空:
根據(jù)我們上面講的流程:那么推測生成的動態(tài)sql語句應該是:
Select * from passwordms,因為if里面的條件每一個成立,所以where也不會添加進去,但是會自動刪除語句中多余的AND或OR
可以看到Mybatis是會根據(jù)我們輸入的查詢條件進行拼接生成動態(tài)的SQL,并且正確無誤。
三、Mybatis的更新操作
4.那么我們繼續(xù)嘗試編寫<set>
、<trim>
的動態(tài)sql語句,情景時:如果想要更新某一個對象,就需要發(fā)送所有的字段給持久化對象,然而在實際應用中,大多數(shù)情況下都是更新某一個或幾個字段。如果更新的每一條數(shù)據(jù)都要將其所有的屬性都更新一遍,那么執(zhí)行效率是非常差的。為了解決更新數(shù)據(jù)的效率問題,MyBatis提供了<set>
元素。
? 除了可以用<trim>
元素來進行更新操作外,也使用<set>
元素來進行更新操作, 在映射文件中使用<set>
元素和<if>
元素組合進行update語句動態(tài)SQL組裝時,如果<set>
元素內(nèi)包含的內(nèi)容都為空,則會出現(xiàn)SQL語法錯誤。因此,在使用<set>
元素進行字段信息更新時,要確保傳入的更新字段不能都為空。
? 除此以外還可以跟前面的<trim>
元素聯(lián)合起來一起使用。其中, <trim>
元素,suffixOverrides屬性指定去除的<trim>
元素所包含內(nèi)容的后綴為逗號 。
好那么我們來編寫我們的Mapper文件:
接下來進行測試類測試:
①:更新多個字段的情況:
? 可以看到嗷,正常數(shù)據(jù)更新了,但是呢,其實是有問題的,不管數(shù)據(jù)是不是更原來的一樣,都會對數(shù)據(jù)庫進行一次更新的操作。
因此我們還需要再改改,還需要加一個數(shù)據(jù)庫對比的判斷,詳細代碼如下:
isDataEqual()方法如下:
// 判斷兩個對象的數(shù)據(jù)是否相等
private boolean isDataEqual(PasswordMS oldData, PasswordMS newData) {
return Objects.equals(oldData.getAccount(), newData.getAccount())
&& Objects.equals(oldData.getWebsiteName(), newData.getWebsiteName());
}
那么保持參數(shù)條件不變的情況下繼續(xù)進行測試:
可以看到測試成功,正確提示!
②嘗試看看單參數(shù)字段修改的情況:
③若是無參數(shù)那么就會報錯,所以一定要確保更新的參數(shù)必須要有,不能全為空。
好的那么更新操作就學到這里了。
四、使用Mybatis的動態(tài)SQL進行復雜操作
使用 <foreach>
元素可以在 MyBatis 中對集合進行迭代操作,通常用于構建 IN 條件的查詢語句。
<foreach>
元素的屬性
屬性 | 說明 |
---|---|
item | 表示集合中每一個元素進行迭代時的別名。該屬性為必選。 |
index | 在List和數(shù)組中,index是元素的序號,在Map中,index是元素的key。該屬性可選。 |
open | 表示foreach語句代碼的開始符號,一般和close=“)”合用。常用在in條件語句中。該屬性可選。 |
separator | 表示元素之間的分隔符,例如,在條件語句中,separator=“,”會自動在元素中間用“,”隔開,避免手動輸入逗號導致SQL錯誤,錯誤示例如in(1,2,)。該屬性可選。 |
close | 表示foreach語句代碼的關閉符號,一般和open="("合用。常用在in條件語句中。該屬性可選。 |
collection | 用于指定遍歷參數(shù)的類型。注意,該屬性必須指定,不同情況下,該屬性的值是不一樣的。 |
<collection>
屬性的取值
? 在遍歷參數(shù)時,<collection>
屬性的值是必須指定的。不同情況下,該屬性的取值也是不一樣的,主要有以下三種情況:List類型、數(shù)值類型、Map類型。
- 若入?yún)閱螀?shù)且參數(shù)類型是一個List,collection屬性值為list。
- 若入?yún)閱螀?shù)且參數(shù)類型是一個數(shù)組,collection屬性值為array。
- 若傳入?yún)?shù)為多參數(shù),就需要把參數(shù)封裝為一個Map進行處理,collection屬性值為Map。若傳入?yún)?shù)為多參數(shù),就需要把參數(shù)封裝為一個Map進行處理,collection屬性值為Map。
5.那么我們繼續(xù)嘗試編寫<foreach>
的動態(tài)sql語句,情景時:要從數(shù)據(jù)表passwordms中查詢出id為1、2、3的賬號信息,就可以利用數(shù)組作為參數(shù),存儲id的屬性值1、2、3,并通過<foreach>
元素迭代數(shù)組完成賬號信息的批量查詢操作。
好那么我們來編寫我們的Mapper文件:
<select id="findByIds" parameterType="list" resultType="PasswordMS">
SELECT * FROM passwordms
WHERE id IN
<foreach item="id" collection="list" open="(" separator="," close=")">
#{id}
</foreach>
</select>
在我們的代碼中,findByIds
是查詢的 ID,parameterType
設置為 list
表示傳入的參數(shù)是一個 List,resultType
設置為 PasswordMS
表示返回結果是 PasswordMS
類型的對象。
編寫測試類:
輸入的參數(shù)類型為同一種,查詢1-3的賬號信息
@Test
void findByIds() {
SqlSession session = MyBatisUtil.createSqlSession();
List<Integer> idList = Arrays.asList(1, 2, 3); // 要查詢的 id 列表
// 傳入?yún)?shù)查詢,返回結果
List<PasswordMS> passwordMSList = session.selectList("findByIds", idList);
for (PasswordMS s : passwordMSList) {
logger.info("id:" + s.getId() + ",賬號:" + s.getAccount() + ",密碼:" + s.getPassword() + ",網(wǎng)站名:" + s.getWebsiteName() + ",網(wǎng)站網(wǎng)址:" + s.getWebsiteURL() + ",網(wǎng)站縮略圖:" + s.getWebsiteImage() + ",賬號描述:" + s.getAccountDescription());
}
// 關閉 session
session.close();
}
可以看出,通過使用 <foreach>
元素就能夠方便地在 MyBatis 中對集合進行迭代操作,構建需要的查詢條件。
總結
這是第三天對SSM框架的學習,深入學習了MyBatis的動態(tài)SQL,它允許我們根據(jù)不同的條件在 SQL 查詢中動態(tài)地構建、拼接和執(zhí)行 SQL 語句。動態(tài) SQL 可以幫助你編寫更加靈活、可復用和動態(tài)的數(shù)據(jù)庫查詢語句,適用于各種復雜的業(yè)務場景。
? 想要跟著學習的可以去我的資源里面找對應的文件下載,我的md文件也會發(fā)上去,項目文件會上傳可以自己跟著學習一下。
PS:sql語句自己編寫┗|`O′|┛ 嗷~~
作者:Stevedash文章來源:http://www.zghlxwxcb.cn/news/detail-667775.html
發(fā)表于:2023年8月23日 16點28分文章來源地址http://www.zghlxwxcb.cn/news/detail-667775.html
注:本文內(nèi)容基于個人學習理解,如有錯誤或疏漏,歡迎指正。感謝閱讀!如果覺得有幫助,請點贊和分享。
到了這里,關于SSM框架的學習與應用(Spring + Spring MVC + MyBatis)-Java EE企業(yè)級應用開發(fā)學習記錄(第三天)動態(tài)SQL的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!