Mybatis之動(dòng)態(tài)SQL使用小結(jié)(全網(wǎng)最新)
1 動(dòng)態(tài)SQL
1.1 引言
MyBatis
令人喜歡的一大特性就是動(dòng)態(tài)SQL
。 在使用JDBC
的過(guò)程中, 根據(jù)條件進(jìn)行SQL
的拼接是很麻煩且很容易出錯(cuò)的。MyBatis
動(dòng)態(tài)SQL
的出現(xiàn), 解決了這個(gè)麻煩。MyBatis
通過(guò)OGNL
來(lái)進(jìn)行動(dòng)態(tài)SQL
的使用的。
目前, 動(dòng)態(tài) SQL 支持以下幾種標(biāo)簽
元素 | 作用 | 備注 |
---|---|---|
if | 判斷語(yǔ)句 | 單條件分支 |
choose(when、otherwise) | 相當(dāng)于 Java 中的 if else | 多條件分支 |
trim(where、set) | 輔助元素 | 用于處理 SQL 拼接問(wèn)題 |
foreach | 循環(huán)語(yǔ)句 | 批量插入, 更新, 查詢時(shí)經(jīng)常用到 |
bind | 創(chuàng)建一個(gè)變量, 并綁定到上下文中 | 用于兼容不同的數(shù)據(jù)庫(kù), 防止 SQL 注入等 |
1.2 數(shù)據(jù)準(zhǔn)備
為了后面的演示, 創(chuàng)建了一個(gè)Maven
項(xiàng)目 mybatis-dynamic, 創(chuàng)建了對(duì)應(yīng)的數(shù)據(jù)庫(kù)和表
DROP TABLE IF EXISTS `student`; CREATE TABLE `student` ( `student_id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '編號(hào)', `name` varchar(20) DEFAULT NULL COMMENT '姓名', `phone` varchar(20) DEFAULT NULL COMMENT '電話', `email` varchar(50) DEFAULT NULL COMMENT '郵箱', `sex` tinyint(4) DEFAULT NULL COMMENT '性別', `locked` tinyint(4) DEFAULT NULL COMMENT '狀態(tài)(0:正常,1:鎖定)', `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '存入數(shù)據(jù)庫(kù)的時(shí)間', `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改的時(shí)間', `delete` int(11) DEFAULT NULL, PRIMARY KEY (`student_id`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='學(xué)生表';
對(duì)應(yīng)的項(xiàng)目結(jié)構(gòu)
1.3 if 標(biāo)簽
if 標(biāo)簽
是我們最常使用的。在查詢、刪除、更新的時(shí)候很可能會(huì)使用到。必須結(jié)合test
屬性聯(lián)合使用
1.3.1 在 WHERE 條件中使用 if 標(biāo)簽
這是常見(jiàn)的一種現(xiàn)象, 我們?cè)谶M(jìn)行按條件查詢的時(shí)候, 可能會(huì)有多種情況。
查詢條件
根據(jù)輸入的學(xué)生信息進(jìn)行條件檢索
- 當(dāng)只輸入用戶名時(shí), 使用用戶名進(jìn)行模糊檢索;
- 當(dāng)只輸入性別時(shí), 使用性別進(jìn)行完全匹配
- 當(dāng)用戶名和性別都存在時(shí), 用這兩個(gè)條件進(jìn)行查詢匹配查詢
動(dòng)態(tài)SQL
<select id="selectByStudentSelective" resultMap="BaseResultMap" parameterType="com.homejim.mybatis.entity.Student"> select <include refid="Base_Column_List" /> from student where 1=1 <if test="name != null and name !=''"> and name like concat('%', #{name}, '%') </if> <if test="sex != null"> and sex=#{sex} </if> </select>
在此 SQL 語(yǔ)句中, where 1=1 是多條件拼接時(shí)的小技巧, 后面的條件查詢就可以都用 and 了。
同時(shí), 我們添加了 if 標(biāo)簽來(lái)處理動(dòng)態(tài) SQL
<if test="name != null and name !=''"> and name like concat('%', #{name}, '%') </if> <if test="sex != null"> and sex=#{sex} </if>
此 if 標(biāo)簽的test
屬性值是一個(gè)符合OGNL
的表達(dá)式, 表達(dá)式可以是true
或false
。如果表達(dá)式返回的是數(shù)值, 則0
為false
,非 0
為true
1.3.2 在 UPDATE 更新列中使用 if 標(biāo)簽
有時(shí)候我們不希望更新所有的字段, 只更新有變化的字段。
示例SQL
<update id="updateByPrimaryKeySelective" parameterType="com.homejim.mybatis.entity.Student"> update student <set> <if test="name != null"> `name` = #{name,jdbcType=VARCHAR}, </if> <if test="phone != null"> phone = #{phone,jdbcType=VARCHAR}, </if> <if test="email != null"> email = #{email,jdbcType=VARCHAR}, </if> <if test="sex != null"> sex = #{sex,jdbcType=TINYINT}, </if> <if test="locked != null"> locked = #{locked,jdbcType=TINYINT}, </if> <if test="createTime != null"> create_time= #{createTime ,jdbcType=TIMESTAMP}, </if> <if test="updateTime != null"> update_time = #{updateTime ,jdbcType=TIMESTAMP}, </if> </set> where student_id = #{studentId,jdbcType=INTEGER}
1.3.3 在 INSERT 動(dòng)態(tài)插入中使用 if 標(biāo)簽
我們插入數(shù)據(jù)庫(kù)中的一條記錄, 不是每一個(gè)字段都有值的, 而是動(dòng)態(tài)變化的。在這時(shí)候使用 if 標(biāo)簽, 可幫我們解決這個(gè)問(wèn)題。
如果只有非空屬性才插入則對(duì)應(yīng)的SQL
<insert id="insertSelective" parameterType="com.homejim.mybatis.entity.Student"> insert into student <trim prefix="(" suffix=")" suffixOverrides=","> <if test="studentId != null"> student_id, </if> <if test="name != null"> `name`, </if> <if test="phone != null"> phone, </if> <if test="email != null"> email, </if> <if test="sex != null"> sex, </if> <if test="locked != null"> locked, </if> <if test="createTime!= null"> create_time, </if> <if test="updateTime != null"> update_time, </if> </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> <if test="studentId != null"> #{studentId,jdbcType=INTEGER}, </if> <if test="name != null"> #{name,jdbcType=VARCHAR}, </if> <if test="phone != null"> #{phone,jdbcType=VARCHAR}, </if> <if test="email != null"> #{email,jdbcType=VARCHAR}, </if> <if test="sex != null"> #{sex,jdbcType=TINYINT}, </if> <if test="locked != null"> #{locked,jdbcType=TINYINT}, </if> <if test="createTime != null"> #{createTime,jdbcType=TIMESTAMP}, </if> <if test="updateTime != null"> #{updateTime,jdbcType=TIMESTAMP}, </if> </trim> </insert>
1.4 choose 標(biāo)簽
choose when otherwise
標(biāo)簽可以幫我們實(shí)現(xiàn)if else
的邏輯。
一個(gè)choose
標(biāo)簽至少有一個(gè)when
, 最多一個(gè)otherwise
假如查詢條件:
- 假設(shè) name 具有唯一性, 查詢一個(gè)學(xué)生
- 當(dāng) studen_id 有值時(shí), 使用 studen_id 進(jìn)行查詢;
- 當(dāng) studen_id 沒(méi)有值時(shí), 使用 name 進(jìn)行查詢;
- 否則返回空
動(dòng)態(tài)SQL
<select id="selectByIdOrName" resultMap="BaseResultMap" parameterType="com.homejim.mybatis.entity.Student"> select <include refid="Base_Column_List" /> from student where 1=1 <choose> <when test="studentId != null"> and student_id=#{studentId} </when> <when test="name != null and name != ''"> and name=#{name} </when> <otherwise> and 1=2 </otherwise> </choose> </select>
1.5 trim(set、where)
這三個(gè)其實(shí)解決的是類(lèi)似的問(wèn)題。如我們?cè)趯?xiě)前面的[在WHERE
條件中使用if 標(biāo)簽 SQL
的時(shí)候, where 1=1 這個(gè)條件我們是不希望存在的。
1.5.1 where
查詢條件:
- 根據(jù)輸入的學(xué)生信息進(jìn)行條件檢索
- 當(dāng)只輸入用戶名時(shí), 使用用戶名進(jìn)行模糊檢索;
- 當(dāng)只輸入性別時(shí), 使用性別進(jìn)行完全匹配
- 當(dāng)用戶名和性別都存在時(shí), 用這兩個(gè)條件進(jìn)行查詢匹配查詢
不使用 where 1=1
動(dòng)態(tài) SQL
<select id="selectByStudentSelectiveWhereTag" resultMap="BaseResultMap" parameterType="com.homejim.mybatis.entity.Student"> select <include refid="Base_Column_List" /> from student <where> <if test="name != null and name !=''"> and name like concat('%', #{name}, '%') </if> <if test="sex != null"> and sex=#{sex} </if> </where> </select>
1.5.2 set
set
標(biāo)簽也類(lèi)似, 在UPDATE
更新列中使用if 標(biāo)簽
中, 如果我們的方法 updateByPrimaryKeySelective 沒(méi)有使用 標(biāo)簽, 那么我們就要想辦法處理字段全為空的條件, 字段不為空的條件等。有了這個(gè), 我們只需要寫(xiě) if 標(biāo)簽即可, 不需要處理類(lèi)似的問(wèn)題。
1.5.3 trim
set
和where
其實(shí)都是trim
標(biāo)簽的一種類(lèi)型, 該兩種功能都可以使用trim
標(biāo)簽進(jìn)行實(shí)現(xiàn)。
1.5.3.1 trim 來(lái)表示 where
如where
標(biāo)簽, 我們也可以寫(xiě)成
<trim prefix="where" prefixOverrides="AND |OR"> </trim>
表示當(dāng)trim
中含有內(nèi)容時(shí), 添加where
, 且第一個(gè)為and
或or
時(shí), 會(huì)將其去掉。而如果沒(méi)有內(nèi)容, 則不添加where
。
1.5.3.2 trim 來(lái)表示 set
相應(yīng)的,set
標(biāo)簽可以如下表示
<trim prefix="set" suffixOverrides=","> </trim>
表示當(dāng)trim
中含有內(nèi)容時(shí), 添加set
, 且最后的內(nèi)容為,
時(shí), 會(huì)將其去掉。而沒(méi)有內(nèi)容, 不添加 set
1.5.3.3 trim 的幾個(gè)屬性
prefix
:當(dāng)trim
元素包含有內(nèi)容時(shí), 增加prefix
所指定的前綴prefixOverrides
:當(dāng)trim
元素包含有內(nèi)容時(shí), 去除prefixOverrides
指定的 前綴
suffix
:當(dāng)trim
元素包含有內(nèi)容時(shí), 增加suffix
所指定的后綴suffixOverrides
:當(dāng)trim
元素包含有內(nèi)容時(shí), 去除suffixOverrides
指定的后綴
1.6 foreach 標(biāo)簽
foreach
標(biāo)簽可以對(duì)數(shù)組,Map
或?qū)崿F(xiàn)Iterable
接口。foreach
中有以下幾個(gè)屬性:
collection
:必填,集合/數(shù)組/Map
的名稱item
:變量名。即從迭代的對(duì)象中取出的每一個(gè)值index
:索引的屬性名。當(dāng)?shù)膶?duì)象為 Map 時(shí), 該值為 Map 中的 Keyopen
:循環(huán)開(kāi)頭的字符串close
:循環(huán)結(jié)束的字符串separator
:每次循環(huán)的分隔符
其他的比較好理解, collection 中的值應(yīng)該怎么設(shè)定呢?
跟接口方法中的參數(shù)相關(guān)。只有一個(gè)數(shù)組參數(shù)或集合參數(shù)默認(rèn)情況:集合collection=list, 數(shù)組是collection=array
使用@Param
來(lái)指定參數(shù)的名稱, 如我們?cè)趨?shù)前@Param("ids")
, 則就填寫(xiě)collection=ids
如果參數(shù)是Map
,指定為Map
中的對(duì)應(yīng)的Key
即可。其實(shí)上面的 @Param 最后也是轉(zhuǎn)化為 Map 的。
如果參數(shù)是對(duì)象使用屬性.屬性
即可。
1.6.1 在 where 中使用 foreach
在 where條件中使用, 如按id集合查詢, 按id集合刪除等。
查詢條件,我們希望查詢用戶 id 集合中的所有用戶信息。
對(duì)應(yīng) SQL
<select id="selectByStudentIdList" resultMap="BaseResultMap"> select <include refid="Base_Column_List" /> from student where student_id in <foreach collection="list" item="id" open="(" close=")" separator="," index="i"> #{id} </foreach> </select>
1.6.2 foreach 實(shí)現(xiàn)批量插入
可以通過(guò)foreach
來(lái)實(shí)現(xiàn)批量插入
動(dòng)態(tài)SQL
<insert id="insertList"> insert into student(name, phone, email, sex, locked) values <foreach collection="list" item="student" separator=","> ( #{student.name}, #{student.phone},#{student.email}, #{student.sex},#{student.locked} ) </foreach> </insert>
1.7 bind 標(biāo)簽
bind
標(biāo)簽是通過(guò)OGNL
表達(dá)式去定義一個(gè)上下文的變量, 這樣方便我們使用。
如在selectByStudentSelective
方法中, 有如下
<if test="name != null and name !=''"> and name like concat('%', #{name}, '%') </if>
在 MySQL 中, 該函數(shù)支持多參數(shù), 但在 Oracle 中只支持兩個(gè)參數(shù)。那么我們可以使用 bind 來(lái)讓該 SQL 達(dá)到支持兩個(gè)數(shù)據(jù)庫(kù)的作用
<if test="name != null and name !=''"> <bind name="nameLike" value="'%'+name+'%'"/> and name like #{nameLike} </if>
到此這篇關(guān)于Mybatis之動(dòng)態(tài)SQL使用講解的文章就介紹到這了,更多相關(guān)Mybatis動(dòng)態(tài)SQL內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- MyBatis中動(dòng)態(tài)SQL的使用指南
- MyBatis中實(shí)現(xiàn)動(dòng)態(tài)SQL標(biāo)簽
- 使用MyBatis的動(dòng)態(tài)SQL注解實(shí)現(xiàn)實(shí)體的CRUD操作代碼
- MyBatis實(shí)現(xiàn)動(dòng)態(tài)SQL的方法
- Mybatis動(dòng)態(tài)Sql標(biāo)簽使用小結(jié)
- MyBatis中的XML實(shí)現(xiàn)和動(dòng)態(tài)SQL實(shí)現(xiàn)示例詳解
- MyBatis映射文件中的動(dòng)態(tài)SQL實(shí)例詳解
- 詳解MyBatis特性之動(dòng)態(tài)SQL
- Mybatis使用注解實(shí)現(xiàn)復(fù)雜動(dòng)態(tài)SQL的方法詳解
- MyBatis的動(dòng)態(tài)攔截sql并修改
- mybatis動(dòng)態(tài)生成sql語(yǔ)句的實(shí)現(xiàn)示例
相關(guān)文章
Spring集成jedis的配置與使用簡(jiǎn)單實(shí)例
今天小編就為大家分享一篇關(guān)于Spring集成jedis的配置與使用簡(jiǎn)單實(shí)例,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-03-03java判斷今天,昨天,前天,不能用秒間隔的簡(jiǎn)單實(shí)例
下面小編就為大家?guī)?lái)一篇java判斷今天,昨天,前天,不能用秒間隔的簡(jiǎn)單實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-03-03java8如何根據(jù)list對(duì)象中的屬性過(guò)濾篩選
這篇文章主要介紹了java8如何根據(jù)list對(duì)象中的屬性過(guò)濾篩選,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05Java實(shí)現(xiàn)簡(jiǎn)單計(jì)算器小程序
這篇文章主要為大家詳細(xì)介紹了Java實(shí)現(xiàn)簡(jiǎn)單計(jì)算器小程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07idea中增強(qiáng)for循環(huán)提示unexpected token問(wèn)題
這篇文章主要介紹了idea中增強(qiáng)for循環(huán)提示unexpected token問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01Java并發(fā)編程中的CyclicBarrier使用解析
這篇文章主要介紹了Java并發(fā)編程中的CyclicBarrier使用解析,CyclicBarrier從字面意思上來(lái)看,循環(huán)柵欄,這篇文章就來(lái)分析下是到底是如何實(shí)現(xiàn)循環(huán)和柵欄的,需要的朋友可以參考下2023-12-12解決Java?結(jié)構(gòu)化數(shù)據(jù)處理開(kāi)源庫(kù)?SPL的問(wèn)題
這篇文章主要介紹了Java?結(jié)構(gòu)化數(shù)據(jù)處理開(kāi)源庫(kù)?SPL的問(wèn)題,Scala提供了較豐富的結(jié)構(gòu)化數(shù)據(jù)計(jì)算函數(shù),但編譯型語(yǔ)言的特點(diǎn),也使它不能成為理想的結(jié)構(gòu)化數(shù)據(jù)計(jì)算類(lèi)庫(kù),對(duì)此內(nèi)容感興趣的朋友一起看看吧2022-03-03