Mybatis中的@Param及動態(tài)SQL詳解
一、@Param注解
@Param 是MyBatis所提供的作為Dao層的注解,作用是用于傳遞參數(shù),從而可以與SQL中的的字段名相對應(yīng)。
首先需要明確一下 @Param 和 @RequestParam 雖然看起來有那么一點相似,但其實是沒有任何關(guān)系的,就像 魚香茄子 和 魚 一樣。
注解 | 來源 |
@Param | org.apache.ibatis.annotations.Param |
@RequestParam | org.springframework.web.bind.annotation.RequestParam |
@RequestParam作用于Controller層,作用是為獲取前端參數(shù),解決的是前后端參數(shù)不一致的問題。@Param作用于Dao層,是為了傳遞多個參數(shù),解決的是可讀性和直觀性;所以它們沒有關(guān)系!
我們簡單看一下使用:
List<PreEndBlogListVO> selectBlogListByTypeId(@Param("typeId") Integer id);
上述 @Param 作用其實就是將 id 重命名為 typeId 。因此,我們可以在xml中這樣使用:
<select id="selectBlogListByTypeId" resultMap="preEndBlogListMap"> select id,title,description from blog where type_id=#{typeId,jdbcType=INTEGER} </select>
并且當(dāng)參數(shù)來自于多個不同POJO時候,使用@Param后,Mybatis會自動檢索參數(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動態(tài)SQL
以前在使用JDBC操作數(shù)據(jù)時,如果查詢條件特別多,將條件串聯(lián)成SQL字符串是一件痛苦的事情。通常的解決方法是寫很多的if-else條件語句對字符串進行拼接,并確保不能忘了空格或在字段的最后省略逗號。MyBatis使用動態(tài)SQL來改善這種情形,動態(tài)SQL是基于OGNL的表達式,可方便我們在SQL語句中實現(xiàn)某些邏輯,簡單說主要是為了解決參數(shù)不固定情況。用于實現(xiàn)動態(tài)SQL的元素如下。
- if:利用if實現(xiàn)簡單的條件選擇
- choose(when,otherwise):相當(dāng)于Java中的switch語句,通常與when和otherwise搭配使用
- set:解決動態(tài)更新語句
- trim:可以靈活的去除多余的關(guān)鍵字
- foreach:迭代一個集合,通常用于in條件
set 標(biāo)簽
使用set標(biāo)簽可以將動態(tài)的配置 SET 關(guān)鍵字,并剔除追加到條件末尾的任何不相關(guān)的逗號。使用 if+set 標(biāo)簽修改后,在進行表單更新的操作中,哪個字段中有值才去更新,如果某項為 null 則不進行更新,而是保持?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"/>其實就是你需要查詢的sql字段,用一個sql標(biāo)簽封裝起來,以后直接引用即可。
一般開發(fā)列表業(yè)務(wù)的查詢條件時,參數(shù)通常不固定,如果有多個查詢條件,通常會使用<where> <if>標(biāo)簽來進行控制。
where標(biāo)簽會自動標(biāo)識其標(biāo)簽內(nèi)是否有返回值,若有,就插入一個where。 <where>標(biāo)簽可以
自動的將第一個滿足的條件前面的邏輯運算符 (or ,and) 去掉,什么意思呢,我們簡單看一下:
假設(shè)此時我們傳入的參數(shù)為:id=0 name="童話" country="中國"
觀察傳入的參數(shù),id傳入0,此條件不會執(zhí)行。name傳入一個合法值,此時name條件成立,但是前面多了一個and關(guān)鍵字,此時<where>標(biāo)簽會自動去掉這個and關(guān)鍵字。
當(dāng)然,有的人可能會說,后面直接給個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)然,這樣肯定是能實現(xiàn)你所想的功能的做法,但這個東西也會帶來一個巨坑,但是這種做法有一個最大的弊端,如果表有索引的話,會導(dǎo)致數(shù)據(jù)表上的索引失效。這樣查詢得效率顯然就會很低下,就像 ${} 和 #{} 多數(shù)情況下可以通用,但是${}就是不能防止SQL注入問題。因此我們還是采用 <where> 標(biāo)簽比較舒適。
如果 where 元素不能滿足你的要求,你也可以通過自定義 trim 元素來定制 where 元素的功能。
2.2 trim和if標(biāo)簽
mybatis的trim標(biāo)簽一般用于去除sql語句中多余的and關(guān)鍵字,逗號,或者給sql語句前拼接 where 、 set 以及 values( 等前綴,或者添加“)“等后綴,可用于選擇性插入、更新、刪除或者條件查詢等操作。
主要有4個屬性:
屬性 | 描述 |
prefix | 給sql語句拼接的前綴 |
suffix | 給sql語句拼接的后綴 |
prefixOverrides | 去除sql語句前面的關(guān)鍵字或者字符,該關(guān)鍵字或者字符由prefixOverrides屬性指定,假設(shè)該屬性指定為"AND",當(dāng)sql語句的開頭為"AND",trim標(biāo)簽將會去除該"AND" |
suffixOverrides | 去除sql語句后面的關(guān)鍵字或者字符,該關(guān)鍵字或者字符由suffixOverrides屬性指定 |
舉個栗子,和 where 元素等價的自定義 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>
上面我們多添加了一個 suffix="order by id" 來實現(xiàn)按照某種規(guī)則排序,下面再看一個稍微復(fù)雜一點的栗子。
<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é)尾多余的逗號。 假設(shè)上述條件只匹配上前三個,可以看到我們的sql將變成
insert into blog(id,title,views,) values(1,"標(biāo)題",22,)
此時插入將會失敗。使用trim標(biāo)簽就可以輕松解決此問題。
2.3 foreach 標(biāo)簽
foreach標(biāo)簽主要有以下參數(shù):
屬性 | 描述 |
item | 循環(huán)體中的具體對象。支持屬性的點路徑訪問,如item.age,item.info.details,在list和數(shù)組中是其中的對象,在map中是value。 |
index | 在list和數(shù)組中,index是元素的序號,在map中,index是元素的key,該參數(shù)可選。 |
open | 表示該語句以什么開始 |
close | 表示該語句以什么結(jié)束 |
separator | 表示元素之間的分隔符,例如在in()的時候,separator=","會自動在元素中間用“,“隔開,避免手動輸入逗號導(dǎo)致sql錯誤,如in(1,2,)這樣。該參數(shù)可選。 |
栗子1:進行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會報如下異常:(可通過調(diào)整MySQL安裝目錄下的 my.ini 文件中 [mysqld] 段的 max_allowed_packet = 1M )
useGeneratedKeys="true" keyProperty="id" 是用于插入之后返回自增主鍵的id的
從結(jié)果可以看出,我們一下插入了兩條數(shù)據(jù),每條數(shù)據(jù)之間使用 “,” 逗號進行分割, separator="," 的作用就是如此。其中 < foreach > 標(biāo)簽內(nèi)部的屬性務(wù)必加上 item。
與上面批量插入對應(yīng)的mapper代碼如下:
int insert(List<User> userList);
當(dāng)然,其實說到批量插入,最粗暴方法就是for循環(huán)直接插入,但是數(shù)據(jù)量越多,耗費的時間將是巨大的。
記錄條數(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 語句新增了:( ? , ? ) ,前后的括號由 open="(" close=")" 進行控制,用 “?” 占位符占位,并通過separator以:“,”隔開,內(nèi)部兩個循環(huán)遍歷出的元素。
栗子3:map參數(shù) < map> 標(biāo)簽需要結(jié)合MyBatis的參數(shù)注解 @Param() 來使用,需要告訴Mybatis配置文件中的 collection="map" 里的map是一個參數(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)簽
這三個標(biāo)簽需要組合在一起使用,類似于 Java 中的 switch、case、default。只有一個條件生效,也就是只執(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>
即使同時添加name和county的值,最終的sql也只會添加第一個屬性值。
三、總結(jié)
使用批量插入執(zhí)行的SQL語句應(yīng)該等價于:
insert into users(name, age, county, date) values (?,?,?,? ),(?,?,?,? ),(?,?,?,? ),(?,?,?,?)
在使用foreach的時候最關(guān)鍵的也是最容易出錯的就是collection屬性,該屬性是必須指定的,但是在不同情況 下,該屬性的值是不一樣的,主要有一下3種情況:
1.如果傳入的是單參數(shù)且參數(shù)類型是一個List的時候,collection屬性值為list
2.如果傳入的是單參數(shù)且參數(shù)類型是一個array數(shù)組的時候,collection的屬性值為array
3.如果傳入的參數(shù)是多個的時候,我們就需要把它們封裝成一個Map了,當(dāng)然單參數(shù)也可以封裝成map
到此這篇關(guān)于Mybatis中的@Param及動態(tài)SQL詳解的文章就介紹到這了,更多相關(guān)Mybatis動態(tài)sql及@Param內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
elasticsearch bucket 之rare terms聚合使用詳解
這篇文章主要為大家介紹了elasticsearch bucket 之rare terms聚合使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11使用SpringBoot實現(xiàn)自動發(fā)送注冊驗證碼郵件功能
一直以來,我都對程序如何自動發(fā)送郵件感到好奇,想象一下,當(dāng)你在某個網(wǎng)站注冊時,輸入郵箱后不久就收到一封帶有驗證碼的郵件,這種體驗既方便又高效,所以本文給大家介紹了如何用?Spring?Boot?實現(xiàn)自動發(fā)送注冊驗證碼郵件,需要的朋友可以參考下2025-04-04idea中創(chuàng)建maven的Javaweb工程并進行配置(圖文教程)
這篇文章主要介紹了idea中創(chuàng)建maven的Javaweb工程并進行配置,本文通過圖文并茂的形式給大家介紹的非常詳細,文中給大家提到了tomcat的運行方法,具有一定的參考借鑒價值,需要的朋友可以參考下2020-02-02Java Apache Shiro安全框架快速開發(fā)詳解流程
Apache Shiro是一個強大且易用的Java安全框架,執(zhí)行身份驗證、授權(quán)、密碼和會話管理。使用Shiro的易于理解的API,您可以快速、輕松地獲得任何應(yīng)用程序,從最小的移動應(yīng)用程序到最大的網(wǎng)絡(luò)和企業(yè)應(yīng)用程序2021-10-10SpringSecurity6.4中一次性令牌登錄(One-Time Token Login)實現(xiàn)
Spring Security為一次性令牌認(rèn)證提供了支持,本文就來介紹一下SpringSecurity6.4中一次性令牌登錄(One-Time Token Login)實現(xiàn),具有一定的參考價值,感興趣的可以了解一下2025-03-03maven的settings.xml、pom.xml配置文件使用詳解
本文詳解了Maven中的配置文件settings.xml和pom.xml,闡述了它們的作用、配置項以及優(yōu)先級順序,settings.xml存在于Maven安裝目錄和用戶目錄下,分別作用于全局和當(dāng)前用戶,pom.xml位于項目根路徑下2024-09-09