湖南做網(wǎng)站 真好磐石網(wǎng)絡(luò)東莞公司網(wǎng)上推廣
在上一篇的:Mybatis 操作數(shù)據(jù)庫的基本 CRUD 以及查詢操作詳析_@糊糊涂涂的博客-CSDN博客中介紹了Mybatis使用固定SQL語句操作數(shù)據(jù),本篇介紹 Mybatis 一個(gè)強(qiáng)大的特性:動態(tài)SQL。
動態(tài) SQL 解決什么問題?
????????那當(dāng)我們要執(zhí)行的業(yè)務(wù)邏輯有很多,比如給成績表中插入一行數(shù)據(jù),對應(yīng)學(xué)生的 “性別” 字段是非必須參數(shù)時(shí),使用動態(tài)SQL就不用寫兩種插入語句(傳與不傳性別);
? ? ? ? 另外,當(dāng)執(zhí)行查詢邏輯時(shí),條件中的參數(shù)個(gè)數(shù)也是不確定的。
? ? ? ? 以上類似傳入?yún)?shù)不確定的情況,都可以使用動態(tài)SQL來解決。
1. <if> 標(biāo)簽:
?????????<if>標(biāo)簽可以用來判斷參數(shù)是否符合預(yù)期值,從而決定 SQL 語句的拼接;
? ? ? ? 下面假設(shè)要給 student 表插入一行數(shù)據(jù),學(xué)生的 sex 字段對應(yīng)的實(shí)體類中 sex 屬性值為null,借助<if>標(biāo)簽判斷是否要在插入時(shí)插入 sex :
Maper 接口:?
@Mapper
public interface StudentMapper {// 新增學(xué)生信息int addStu(Student student);
}
?插入語句:
<mapper namespace="com.example.demo.mapper.StudentMapper"><insert id="addStu">insert into student (uid, name<if test="sex != null and sex != '' ">,sex</if>,score) values (#{uid}, #{name}<if test="sex != null and sex != '' ">,#{sex}</if>,#{score})</insert>
</mapper>
?測試方法:
@SpringBootTest
class StudentMapperTest {@Autowiredprivate StudentMapper studentMapper;@Transactional@Testvoid addStu() {Student student = new Student();student.setUid(1);student.setName("張三");student.setScore(98);// 傳入的實(shí)體對象中不包含 sexint result = studentMapper.addStu(student);System.out.println("插入成功:" + result);}
}
!!!使用時(shí)要注意區(qū)分屬性和字段:
test 里要判斷的是“屬性” —— 來自實(shí)體類對象;
其他的是字段 —— 和數(shù)據(jù)庫對應(yīng);
2. <trim> 標(biāo)簽:
? ? ? ? <trim>標(biāo)簽還會結(jié)合<if>標(biāo)簽一起使用,它有字面意思“修剪”的含義。
? ? ? ? 當(dāng)SQL語句中有很多個(gè)非必傳參數(shù)時(shí),一旦只傳其中個(gè)別參數(shù),就會導(dǎo)致殘留逗號或括號等情況,導(dǎo)致SQL語句出現(xiàn)錯(cuò)誤;<trim> 標(biāo)簽就會根據(jù)實(shí)際情況,去掉殘留不必要的內(nèi)容。
<trim>標(biāo)簽的四個(gè)參數(shù):
????????可根據(jù)場景添加
- prefix:表示整個(gè)語句塊,以prefix的值作為前綴
- suffix:表示整個(gè)語句塊,以suffix的值作為后綴
- prefixOverrides:表示整個(gè)語句塊要去除掉的前綴
- suffixOverrides:表示整個(gè)語句塊要去除掉的后綴
下面演示:插入一條學(xué)生信息,但參數(shù)只傳學(xué)生名,就會導(dǎo)致字段后面多出一個(gè)逗號,同時(shí)如果不穿參數(shù),又會多出一對括號,借助 trim 來修改:
Mapper 接口:?
@Mapper
public interface StudentMapper {// 只插入學(xué)生姓名int addStuOnlyName(Student student);
}
SQL 語句:
<insert id="addStuOnlyName">insert into student<trim prefix="(" suffix=")" suffixOverrides=","><if test="uid != null and uid != '' ">uid,</if><if test="name != null and name != '' ">name,</if><if test="sex != null and sex != '' ">sex,</if><if test="score != null and score != '' ">score</if></trim>values<trim prefix="(" suffix=")" suffixOverrides=","><if test="uid != null and uid != '' ">#{uid},</if><if test="name != null and name != '' ">#{name},</if><if test="sex != null and sex != '' ">#{sex},</if><if test="score != null and score != '' ">#{score}</if></trim></insert>
單元測試:
@Testvoid addStuOnlyName() {Student student = new Student();student.setName("李四");int result = studentMapper.addStuOnlyName(student);System.out.println("插入成功:" + result);}
?
3. <where> 標(biāo)簽:
? ? ? ? 直接借助示例演示:根據(jù)學(xué)生 uid 或 學(xué)生名 來查詢一條學(xué)生信息,這里的兩個(gè)查詢條件都是非必傳的。
? ? ? ? ① 如果查詢時(shí)只給了其中一個(gè)條件,那么 where 后面連接時(shí)的 "and" 就會被多出來;
? ? ? ? ② 如果兩個(gè)條件都不穿,那么 "where" 就會被多出來;
針對第一種情況:可以使用<trim> 標(biāo)簽去后綴的方式去掉 and,and 放在參數(shù)的后面;
針對第二種情況:解決辦法很多種:
- where 后添加 1=1,and 放在每個(gè)條件參數(shù)前面,使用<trim>去前綴去掉and;
? ? ? ? (但這種寫法很冗余,不是好辦法)
- where 作為<trim>標(biāo)簽的前綴,只有<trim>里有代碼,才會自動加上前綴 where,再按照去后綴的方式去掉 and;
- 使用 <where> 標(biāo)簽,專門解決這種場景:
? ? <where> 里面有內(nèi)容,就會自動生成 where,沒有就不生成。
??????????????? ?同時(shí):如果有多出來的 and ,它也會按照去前綴的方式去掉
????????
?
4. <set> 標(biāo)簽:
? ? ? ? <set> 標(biāo)簽用于修改場景,<set>標(biāo)簽也是包著所有參數(shù),如果沒有內(nèi)容,就不加 set,但沒有set語句對于 mysql 是錯(cuò)誤的,所以至少要傳一個(gè)參數(shù)
? ? ? ? <set> 會自動去掉多余的逗號
<update id="updateStu">update student<set><if test="uid != null and uid > 0">uid = #{uid},</if><if test="name != null and name != '' ">name = #{name},</if><if test="sex != null and sex != '' ">sex = #{sex},</if><if test="score != null and score > 0">score = #{score}</if></set>where uid = #{uid}</update>
5. <foreach> 標(biāo)簽:
? ? ? ? <foreach> 標(biāo)簽用于遍歷傳入的集合,它有五個(gè)可選項(xiàng):
- collection:綁定方法參數(shù)中的集合,如 List,Set,Map或數(shù)組對象
- item:遍歷時(shí)的每?個(gè)對象
- open:語句塊開頭的字符串
- close:語句塊結(jié)束的字符串
- separator:每次遍歷的對象之間間隔的字符串
// 根據(jù) uid 批量刪除int deleteByUids(List<Integer> uidList);
<delete id="deleteByUids">delete from studentwhere uid in<foreach collection="uidList" item="uid" open="(" close=")" separator=",">#{uid}</foreach></delete>
?