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