Mybatis中的@Param及動(dòng)態(tài)SQL詳解
一、@Param注解
@Param 是MyBatis所提供的作為Dao層的注解,作用是用于傳遞參數(shù),從而可以與SQL中的的字段名相對(duì)應(yīng)。
首先需要明確一下 @Param 和 @RequestParam 雖然看起來有那么一點(diǎn)相似,但其實(shí)是沒有任何關(guān)系的,就像 魚香茄子 和 魚 一樣。
| 注解 | 來源 |
| @Param | org.apache.ibatis.annotations.Param |
| @RequestParam | org.springframework.web.bind.annotation.RequestParam |
@RequestParam作用于Controller層,作用是為獲取前端參數(shù),解決的是前后端參數(shù)不一致的問題。@Param作用于Dao層,是為了傳遞多個(gè)參數(shù),解決的是可讀性和直觀性;所以它們沒有關(guān)系!
我們簡單看一下使用:
List<PreEndBlogListVO> selectBlogListByTypeId(@Param("typeId") Integer id);上述 @Param 作用其實(shí)就是將 id 重命名為 typeId 。因此,我們可以在xml中這樣使用:
<select id="selectBlogListByTypeId" resultMap="preEndBlogListMap">
select id,title,description from blog where type_id=#{typeId,jdbcType=INTEGER}
</select>并且當(dāng)參數(shù)來自于多個(gè)不同POJO時(shí)候,使用@Param后,Mybatis會(huì)自動(dòng)檢索參數(shù)類型,也就是不用再在xml中配置 parameterType 屬性了。
public List<Role> findRoleByMix(@Param("roleP") RoleParam role, @Param("permissionP") PermissionParam permission);xml配置:
<select id="findRoleByMix" resultType="role">
SELECT id,name FROM t_role
WHERE roleName=#{roleP.roleName}
AND note=#{rolep.note}
AND level=#{permissionP.level}
<select>二、Mybatis動(dòng)態(tài)SQL
以前在使用JDBC操作數(shù)據(jù)時(shí),如果查詢條件特別多,將條件串聯(lián)成SQL字符串是一件痛苦的事情。通常的解決方法是寫很多的if-else條件語句對(duì)字符串進(jìn)行拼接,并確保不能忘了空格或在字段的最后省略逗號(hào)。MyBatis使用動(dòng)態(tài)SQL來改善這種情形,動(dòng)態(tài)SQL是基于OGNL的表達(dá)式,可方便我們?cè)赟QL語句中實(shí)現(xiàn)某些邏輯,簡單說主要是為了解決參數(shù)不固定情況。用于實(shí)現(xiàn)動(dòng)態(tài)SQL的元素如下。
- if:利用if實(shí)現(xiàn)簡單的條件選擇
- choose(when,otherwise):相當(dāng)于Java中的switch語句,通常與when和otherwise搭配使用
- set:解決動(dòng)態(tài)更新語句
- trim:可以靈活的去除多余的關(guān)鍵字
- foreach:迭代一個(gè)集合,通常用于in條件
set 標(biāo)簽
使用set標(biāo)簽可以將動(dòng)態(tài)的配置 SET 關(guān)鍵字,并剔除追加到條件末尾的任何不相關(guān)的逗號(hào)。使用 if+set 標(biāo)簽修改后,在進(jìn)行表單更新的操作中,哪個(gè)字段中有值才去更新,如果某項(xiàng)為 null 則不進(jìn)行更新,而是保持?jǐn)?shù)據(jù)庫原值。
<update id="updateSet" parameterType="com.dl.POJO.User">
update user
<set>
<if test="name != null and name != ''">
name = #{name},
</if>
<if test="county != null and county != ''">
county = #{county},
</if>
</set>
where id = #{id}
</update>大多數(shù)標(biāo)簽我們都是結(jié)合在一起使用的,常見主要是為了解決參數(shù)不固定情況。
2.1 if 和 where 標(biāo)簽
<sql id="params">
id, name,country,sex,birth,tel
</sql>
<select id="selectByConditions" resultType="com.dl.POJO.User">
select <include refid="params"/> from user
<where>
<if test="id != null and id != 0">
AND id = #{id}
</if>
<if test="name != null and name != ''">
AND name = #{name}
</if>
<if test="county != null and county != ''">
AND county = #{county}
</if>
</where>
</select><include refid="params"/>其實(shí)就是你需要查詢的sql字段,用一個(gè)sql標(biāo)簽封裝起來,以后直接引用即可。
一般開發(fā)列表業(yè)務(wù)的查詢條件時(shí),參數(shù)通常不固定,如果有多個(gè)查詢條件,通常會(huì)使用<where> <if>標(biāo)簽來進(jìn)行控制。
where標(biāo)簽會(huì)自動(dòng)標(biāo)識(shí)其標(biāo)簽內(nèi)是否有返回值,若有,就插入一個(gè)where。 <where>標(biāo)簽可以
自動(dòng)的將第一個(gè)滿足的條件前面的邏輯運(yùn)算符 (or ,and) 去掉,什么意思呢,我們簡單看一下:
假設(shè)此時(shí)我們傳入的參數(shù)為:id=0 name="童話" country="中國"
觀察傳入的參數(shù),id傳入0,此條件不會(huì)執(zhí)行。name傳入一個(gè)合法值,此時(shí)name條件成立,但是前面多了一個(gè)and關(guān)鍵字,此時(shí)<where>標(biāo)簽會(huì)自動(dòng)去掉這個(gè)and關(guān)鍵字。
當(dāng)然,有的人可能會(huì)說,后面直接給個(gè)where 1=1不就可以不用<where>標(biāo)簽了么。如下所示:
<select id="findActiveBlogLike" resultType="Blog">
select * from blog
where 1=1
<if test="state != null">
and state = #{state}
</if>
</select>當(dāng)然,這樣肯定是能實(shí)現(xiàn)你所想的功能的做法,但這個(gè)東西也會(huì)帶來一個(gè)巨坑,但是這種做法有一個(gè)最大的弊端,如果表有索引的話,會(huì)導(dǎo)致數(shù)據(jù)表上的索引失效。這樣查詢得效率顯然就會(huì)很低下,就像 ${} 和 #{} 多數(shù)情況下可以通用,但是${}就是不能防止SQL注入問題。因此我們還是采用 <where> 標(biāo)簽比較舒適。
如果 where 元素不能滿足你的要求,你也可以通過自定義 trim 元素來定制 where 元素的功能。
2.2 trim和if標(biāo)簽
mybatis的trim標(biāo)簽一般用于去除sql語句中多余的and關(guān)鍵字,逗號(hào),或者給sql語句前拼接 where 、 set 以及 values( 等前綴,或者添加“)“等后綴,可用于選擇性插入、更新、刪除或者條件查詢等操作。
主要有4個(gè)屬性:
| 屬性 | 描述 |
| prefix | 給sql語句拼接的前綴 |
| suffix | 給sql語句拼接的后綴 |
| prefixOverrides | 去除sql語句前面的關(guān)鍵字或者字符,該關(guān)鍵字或者字符由prefixOverrides屬性指定,假設(shè)該屬性指定為"AND",當(dāng)sql語句的開頭為"AND",trim標(biāo)簽將會(huì)去除該"AND" |
| suffixOverrides | 去除sql語句后面的關(guān)鍵字或者字符,該關(guān)鍵字或者字符由suffixOverrides屬性指定 |
舉個(gè)栗子,和 where 元素等價(jià)的自定義 trim 元素為:
<select id="getUserList" resultType="com.dl.POJO.User">
select <include refid="params"/> from user
<trim prefix="where" prefixOverrides="and | or" suffix="order by id">
<if test="id != null and id != 0">
AND id = #{id}
</if>
<if test="name != null and name != ''">
AND name = #{name}
</if>
<if test="county != null and county != ''">
AND county = #{county}
</if>
</trim>
</select>上面我們多添加了一個(gè) suffix="order by id" 來實(shí)現(xiàn)按照某種規(guī)則排序,下面再看一個(gè)稍微復(fù)雜一點(diǎn)的栗子。
<insert id="insertSelective" parameterType="com.dl.blog.pojo.Blog">
insert into blog
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">
id,
</if>
<if test="title != null">
title,
</if>
<if test="views != null">
views,
</if>
<if test="createTime != null">
create_time,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">
#{id,jdbcType=INTEGER},
</if>
<if test="title != null">
#{title,jdbcType=VARCHAR},
</if>
<if test="views != null">
#{views,jdbcType=INTEGER},
</if>
<if test="createTime != null">
#{createTime,jdbcType=TIMESTAMP},
</if>
</trim>
</insert>其中最重要的就是 suffixOverrides="," ,表示去除sql語句結(jié)尾多余的逗號(hào)。 假設(shè)上述條件只匹配上前三個(gè),可以看到我們的sql將變成
insert into blog(id,title,views,) values(1,"標(biāo)題",22,)
此時(shí)插入將會(huì)失敗。使用trim標(biāo)簽就可以輕松解決此問題。
2.3 foreach 標(biāo)簽
foreach標(biāo)簽主要有以下參數(shù):
| 屬性 | 描述 |
| item | 循環(huán)體中的具體對(duì)象。支持屬性的點(diǎn)路徑訪問,如item.age,item.info.details,在list和數(shù)組中是其中的對(duì)象,在map中是value。 |
| index | 在list和數(shù)組中,index是元素的序號(hào),在map中,index是元素的key,該參數(shù)可選。 |
| open | 表示該語句以什么開始 |
| close | 表示該語句以什么結(jié)束 |
| separator | 表示元素之間的分隔符,例如在in()的時(shí)候,separator=","會(huì)自動(dòng)在元素中間用“,“隔開,避免手動(dòng)輸入逗號(hào)導(dǎo)致sql錯(cuò)誤,如in(1,2,)這樣。該參數(shù)可選。 |
栗子1:進(jìn)行l(wèi)ist批量插入
<insert id="dynamicSqlInsertList" useGeneratedKeys="true" keyProperty="id" parameterType="java.util.List">
insert into users (name, age, county, date)
values
<foreach collection="list" item="user" separator="," >
(#{user.name}, #{user.age}, #{user.county}, #{user.date})
</foreach>
</insert>特別注意:Mysql默認(rèn)接受sql的大小是 1048576(1M), 即第三種方式若數(shù)據(jù)量超過1M會(huì)報(bào)如下異常:(可通過調(diào)整MySQL安裝目錄下的 my.ini 文件中 [mysqld] 段的 max_allowed_packet = 1M )
useGeneratedKeys="true" keyProperty="id" 是用于插入之后返回自增主鍵的id的

從結(jié)果可以看出,我們一下插入了兩條數(shù)據(jù),每條數(shù)據(jù)之間使用 “,” 逗號(hào)進(jìn)行分割, separator="," 的作用就是如此。其中 < foreach > 標(biāo)簽內(nèi)部的屬性務(wù)必加上 item。
與上面批量插入對(duì)應(yīng)的mapper代碼如下:
int insert(List<User> userList);
當(dāng)然,其實(shí)說到批量插入,最粗暴方法就是for循環(huán)直接插入,但是數(shù)據(jù)量越多,耗費(fèi)的時(shí)間將是巨大的。
| 記錄條數(shù) | 普通for循環(huán) | Mybatis batch | foreach標(biāo)簽 |
| 500條 | 7742 | 7388 | 622 |
| 1000條 | 15290 | 15078 | 746 |
| 5000條 | 78011 | 177350 | 1172 |
| 10000條 | 397472 | 201180 | 1205 |
栗子2:list集合參數(shù)
<select id="dynamicSqlSelectList" resultType="com.dl.POJO.User">
SELECT * from user WHERE id in
<foreach collection="list" item="id" open="(" close=")" separator="," >
#{id}
</foreach>
</select>
可以看出我們的 SQL 語句新增了:( ? , ? ) ,前后的括號(hào)由 open="(" close=")" 進(jìn)行控制,用 “?” 占位符占位,并通過separator以:“,”隔開,內(nèi)部兩個(gè)循環(huán)遍歷出的元素。
栗子3:map參數(shù) < map> 標(biāo)簽需要結(jié)合MyBatis的參數(shù)注解 @Param() 來使用,需要告訴Mybatis配置文件中的 collection="map" 里的map是一個(gè)參數(shù):
<select id="dynamicSqlSelectMap" resultType="com.lks.bean.User">
select * from users WHERE
<foreach collection="map" index="key" item="value" separator="=">
${key} = #{value}
</foreach>
</select>2.4 choose、when、otherwise 標(biāo)簽
這三個(gè)標(biāo)簽需要組合在一起使用,類似于 Java 中的 switch、case、default。只有一個(gè)條件生效,也就是只執(zhí)行滿足的條件 when,沒有滿足的條件就執(zhí)行 otherwise,表示默認(rèn)條件。
<select id="dynamicSql2" resultType="com.lks.domain.User">
select * from users
<where>
<choose>
<when test="name != null and name != ''">
AND name = #{name}
</when>
<when test="county != null and county != ''">
AND county = #{county}
</when>
<otherwise>
AND id = #{id}
</otherwise>
</choose>
</where>
</select>即使同時(shí)添加name和county的值,最終的sql也只會(huì)添加第一個(gè)屬性值。
三、總結(jié)
使用批量插入執(zhí)行的SQL語句應(yīng)該等價(jià)于:
insert into users(name, age, county, date) values (?,?,?,? ),(?,?,?,? ),(?,?,?,? ),(?,?,?,?)
在使用foreach的時(shí)候最關(guān)鍵的也是最容易出錯(cuò)的就是collection屬性,該屬性是必須指定的,但是在不同情況 下,該屬性的值是不一樣的,主要有一下3種情況:
1.如果傳入的是單參數(shù)且參數(shù)類型是一個(gè)List的時(shí)候,collection屬性值為list
2.如果傳入的是單參數(shù)且參數(shù)類型是一個(gè)array數(shù)組的時(shí)候,collection的屬性值為array
3.如果傳入的參數(shù)是多個(gè)的時(shí)候,我們就需要把它們封裝成一個(gè)Map了,當(dāng)然單參數(shù)也可以封裝成map
到此這篇關(guān)于Mybatis中的@Param及動(dòng)態(tài)SQL詳解的文章就介紹到這了,更多相關(guān)Mybatis動(dòng)態(tài)sql及@Param內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- MyBatis實(shí)現(xiàn)動(dòng)態(tài)SQL的方法
- Mybatis之動(dòng)態(tài)SQL使用小結(jié)(全網(wǎng)最新)
- Mybatis動(dòng)態(tài)Sql標(biāo)簽使用小結(jié)
- MyBatis映射文件中的動(dòng)態(tài)SQL實(shí)例詳解
- 詳解MyBatis特性之動(dòng)態(tài)SQL
- Mybatis使用XML實(shí)現(xiàn)動(dòng)態(tài)sql的示例代碼
- Mybatis動(dòng)態(tài)sql中@Param使用詳解
- mybatis動(dòng)態(tài)sql之新增與更新方式
- MyBatis中實(shí)現(xiàn)動(dòng)態(tài)SQL標(biāo)簽
相關(guān)文章
Springboot整合RabbitMq測試TTL的方法詳解
這篇文章主要介紹了Springboot整合RabbitMq測試TTL的設(shè)置,設(shè)置TTL一般由兩種設(shè)置方法,設(shè)置整個(gè)隊(duì)列的過期時(shí)間另一種設(shè)置單個(gè)消息的過期時(shí)間,通過示例圖文相結(jié)合給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-03-03
詳解JAVA 設(shè)計(jì)模式之狀態(tài)模式
這篇文章主要介紹了JAVA 狀態(tài)模式的的相關(guān)資料,文中講解的非常細(xì)致,幫助大家更好的學(xué)習(xí)理解JAVA 設(shè)計(jì)模式,感興趣的朋友可以了解下2020-06-06
解釋為什么Java中“1000==1000”為false而”100==100“為true
在日常編程中,我們經(jīng)常遇到一些看似簡單卻隱藏著復(fù)雜邏輯的問題,這篇文章主要介紹了解釋為什么Java中“1000==1000”為false而”100==100“為true,需要的朋友可以參考下2024-01-01
java中@ConfigurationProperties失效的問題解決
在Java開發(fā)中,使用@ConfigurationProperties注解讀取配置文件時(shí),如果配置類中的屬性設(shè)置為static,將無法正確讀取配置值,本文就來介紹一下具體解決方法,感興趣的可以了解一下2024-09-09
springboot整合日志處理Logback的實(shí)現(xiàn)示例
Logback是由log4j創(chuàng)始人設(shè)計(jì)的又一個(gè)開源日志組件,本文主要介紹了springboot整合日志處理Logback,文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-01-01
基于SpringBoot和Vue3的博客平臺(tái)文章詳情與評(píng)論功能實(shí)現(xiàn)
在前面的教程中,我們已經(jīng)實(shí)現(xiàn)了基于Spring Boot和Vue3的發(fā)布、編輯、刪除文章功能以及文章列表與分頁功能。本教程將引導(dǎo)您實(shí)現(xiàn)博客平臺(tái)的文章詳情與評(píng)論功能,需要的朋友可以參考一下2023-04-04
Java+MySQL實(shí)現(xiàn)設(shè)計(jì)優(yōu)惠券系統(tǒng)
這篇文章主要介紹了Java+MySQL實(shí)現(xiàn)設(shè)計(jì)優(yōu)惠券系統(tǒng),文章基于Java與MySQL的相關(guān)資料展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-05-05

