Mybatis中的@Param及動(dòng)態(tài)SQL詳解
一、@Param注解
@Param 是MyBatis所提供的作為Dao層的注解,作用是用于傳遞參數(shù),從而可以與SQL中的的字段名相對(duì)應(yīng)。
首先需要明確一下 @Param 和 @RequestParam 雖然看起來(lái)有那么一點(diǎn)相似,但其實(shí)是沒(méi)有任何關(guān)系的,就像 魚(yú)香茄子 和 魚(yú) 一樣。
注解 | 來(lái)源 |
@Param | org.apache.ibatis.annotations.Param |
@RequestParam | org.springframework.web.bind.annotation.RequestParam |
@RequestParam作用于Controller層,作用是為獲取前端參數(shù),解決的是前后端參數(shù)不一致的問(wèn)題。@Param作用于Dao層,是為了傳遞多個(gè)參數(shù),解決的是可讀性和直觀性;所以它們沒(méi)有關(guān)系!
我們簡(jiǎ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ù)來(lái)自于多個(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字符串是一件痛苦的事情。通常的解決方法是寫(xiě)很多的if-else條件語(yǔ)句對(duì)字符串進(jìn)行拼接,并確保不能忘了空格或在字段的最后省略逗號(hào)。MyBatis使用動(dòng)態(tài)SQL來(lái)改善這種情形,動(dòng)態(tài)SQL是基于OGNL的表達(dá)式,可方便我們?cè)赟QL語(yǔ)句中實(shí)現(xiàn)某些邏輯,簡(jiǎn)單說(shuō)主要是為了解決參數(shù)不固定情況。用于實(shí)現(xiàn)動(dòng)態(tài)SQL的元素如下。
- if:利用if實(shí)現(xiàn)簡(jiǎn)單的條件選擇
- choose(when,otherwise):相當(dāng)于Java中的switch語(yǔ)句,通常與when和otherwise搭配使用
- set:解決動(dòng)態(tài)更新語(yǔ)句
- 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ù)庫(kù)原值。
<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é)合在一起使用的,常見(jiàn)主要是為了解決參數(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)簽封裝起來(lái),以后直接引用即可。
一般開(kāi)發(fā)列表業(yè)務(wù)的查詢條件時(shí),參數(shù)通常不固定,如果有多個(gè)查詢條件,通常會(huì)使用<where> <if>標(biāo)簽來(lái)進(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) 去掉,什么意思呢,我們簡(jiǎn)單看一下:
假設(shè)此時(shí)我們傳入的參數(shù)為:id=0 name="童話" country="中國(guó)"
觀察傳入的參數(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ì)說(shuō),后面直接給個(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ì)帶來(lái)一個(gè)巨坑,但是這種做法有一個(gè)最大的弊端,如果表有索引的話,會(huì)導(dǎo)致數(shù)據(jù)表上的索引失效。這樣查詢得效率顯然就會(huì)很低下,就像 ${} 和 #{} 多數(shù)情況下可以通用,但是${}就是不能防止SQL注入問(wèn)題。因此我們還是采用 <where> 標(biāo)簽比較舒適。
如果 where 元素不能滿足你的要求,你也可以通過(guò)自定義 trim 元素來(lái)定制 where 元素的功能。
2.2 trim和if標(biāo)簽
mybatis的trim標(biāo)簽一般用于去除sql語(yǔ)句中多余的and關(guān)鍵字,逗號(hào),或者給sql語(yǔ)句前拼接 where 、 set 以及 values( 等前綴,或者添加“)“等后綴,可用于選擇性插入、更新、刪除或者條件查詢等操作。
主要有4個(gè)屬性:
屬性 | 描述 |
prefix | 給sql語(yǔ)句拼接的前綴 |
suffix | 給sql語(yǔ)句拼接的后綴 |
prefixOverrides | 去除sql語(yǔ)句前面的關(guān)鍵字或者字符,該關(guān)鍵字或者字符由prefixOverrides屬性指定,假設(shè)該屬性指定為"AND",當(dāng)sql語(yǔ)句的開(kāi)頭為"AND",trim標(biāo)簽將會(huì)去除該"AND" |
suffixOverrides | 去除sql語(yǔ)句后面的關(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" 來(lái)實(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語(yǔ)句結(jié)尾多余的逗號(hào)。 假設(shè)上述條件只匹配上前三個(gè),可以看到我們的sql將變成
insert into blog(id,title,views,) values(1,"標(biāo)題",22,)
此時(shí)插入將會(huì)失敗。使用trim標(biāo)簽就可以輕松解決此問(wèn)題。
2.3 foreach 標(biāo)簽
foreach標(biāo)簽主要有以下參數(shù):
屬性 | 描述 |
item | 循環(huán)體中的具體對(duì)象。支持屬性的點(diǎn)路徑訪問(wèn),如item.age,item.info.details,在list和數(shù)組中是其中的對(duì)象,在map中是value。 |
index | 在list和數(shù)組中,index是元素的序號(hào),在map中,index是元素的key,該參數(shù)可選。 |
open | 表示該語(yǔ)句以什么開(kāi)始 |
close | 表示該語(yǔ)句以什么結(jié)束 |
separator | 表示元素之間的分隔符,例如在in()的時(shí)候,separator=","會(huì)自動(dòng)在元素中間用“,“隔開(kāi),避免手動(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ù)量超過(guò)1M會(huì)報(bào)如下異常:(可通過(guò)調(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í)說(shuō)到批量插入,最粗暴方法就是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 語(yǔ)句新增了:( ? , ? ) ,前后的括號(hào)由 open="(" close=")" 進(jìn)行控制,用 “?” 占位符占位,并通過(guò)separator以:“,”隔開(kāi),內(nèi)部?jī)蓚€(gè)循環(huán)遍歷出的元素。
栗子3:map參數(shù) < map> 標(biāo)簽需要結(jié)合MyBatis的參數(shù)注解 @Param() 來(lái)使用,需要告訴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,沒(méi)有滿足的條件就執(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ǔ)句應(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)文章
elasticsearch bucket 之rare terms聚合使用詳解
這篇文章主要為大家介紹了elasticsearch bucket 之rare terms聚合使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11使用SpringBoot實(shí)現(xiàn)自動(dòng)發(fā)送注冊(cè)驗(yàn)證碼郵件功能
一直以來(lái),我都對(duì)程序如何自動(dòng)發(fā)送郵件感到好奇,想象一下,當(dāng)你在某個(gè)網(wǎng)站注冊(cè)時(shí),輸入郵箱后不久就收到一封帶有驗(yàn)證碼的郵件,這種體驗(yàn)既方便又高效,所以本文給大家介紹了如何用?Spring?Boot?實(shí)現(xiàn)自動(dòng)發(fā)送注冊(cè)驗(yàn)證碼郵件,需要的朋友可以參考下2025-04-04mybatisPlus實(shí)現(xiàn)倒序拼接字符串
這篇文章主要介紹了mybatisPlus實(shí)現(xiàn)倒序拼接字符串方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01idea中創(chuàng)建maven的Javaweb工程并進(jìn)行配置(圖文教程)
這篇文章主要介紹了idea中創(chuàng)建maven的Javaweb工程并進(jìn)行配置,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),文中給大家提到了tomcat的運(yùn)行方法,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-02-02Java Apache Shiro安全框架快速開(kāi)發(fā)詳解流程
Apache Shiro是一個(gè)強(qiáng)大且易用的Java安全框架,執(zhí)行身份驗(yàn)證、授權(quán)、密碼和會(huì)話管理。使用Shiro的易于理解的API,您可以快速、輕松地獲得任何應(yīng)用程序,從最小的移動(dòng)應(yīng)用程序到最大的網(wǎng)絡(luò)和企業(yè)應(yīng)用程序2021-10-10SpringSecurity6.4中一次性令牌登錄(One-Time Token Login)實(shí)現(xiàn)
Spring Security為一次性令牌認(rèn)證提供了支持,本文就來(lái)介紹一下SpringSecurity6.4中一次性令牌登錄(One-Time Token Login)實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2025-03-03maven的settings.xml、pom.xml配置文件使用詳解
本文詳解了Maven中的配置文件settings.xml和pom.xml,闡述了它們的作用、配置項(xiàng)以及優(yōu)先級(jí)順序,settings.xml存在于Maven安裝目錄和用戶目錄下,分別作用于全局和當(dāng)前用戶,pom.xml位于項(xiàng)目根路徑下2024-09-09