這個(gè)專欄給大家介紹一下 Java 家族的核心產(chǎn)品 - SSM 框架
JavaEE 進(jìn)階專欄Java 語(yǔ)言能走到現(xiàn)在 , 仍然屹立不衰的原因 , 有一部分就是因?yàn)?SSM 框架的存在
接下來(lái) , 博主會(huì)帶大家了解一下 Spring、Spring Boot、Spring MVC、MyBatis 相關(guān)知識(shí)點(diǎn)
并且?guī)ьI(lǐng)大家進(jìn)行環(huán)境的配置 , 讓大家真正用好框架、學(xué)懂框架
來(lái)上一篇文章復(fù)習(xí)一下吧
點(diǎn)擊即可跳轉(zhuǎn)到前置文章
CSDN 平臺(tái)觀感有限 , 可以私聊作者獲取源筆記鏈接
七 . 查詢操作
7.1 單表查詢
7.1.1 參數(shù)占位符 : #{} 和 ${}
我們之前介紹過(guò) : 傳參的時(shí)候使用 #{}
這種情況適用于 99.99% 的情況 , 但是還有 0.01% 的情況是使用不了 #{} 的
這也是一道非常經(jīng)典的面試題 !!!
我們通過(guò) getUserById 來(lái)舉例 :
[外鏈圖片轉(zhuǎn)存中…(img-lZFq1WJB-1692756514196)]
我們通過(guò)單元測(cè)試來(lái)看一下 , 目前這個(gè)查詢有無(wú)問(wèn)題
那我們接下來(lái) , 把 #{id} 替換成 ${id}
[外鏈圖片轉(zhuǎn)存中…(img-WpAtZwlE-1692756514197)]
這不都運(yùn)行成功了嗎 ? 那他們倆能有什么區(qū)別 ?
在 int 類型的傳參中 , #{} 和 ${} 是一樣的 , 但是二者在字符串類型的時(shí)候就不同了 .
接下來(lái) , 給大家實(shí)驗(yàn)一下 , 我們?cè)?UserMapper 里面添加一個(gè)方法 : 根據(jù)用戶名查詢用戶
package com.example.demo.mapper;
import com.example.demo.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
// MyBatis 接口,此注解一定不能省略
@Mapper
public interface UserMapper {
// 根據(jù)用戶姓名查詢用戶
public UserInfo getUserByName(@Param("username") String username);
}
[外鏈圖片轉(zhuǎn)存中…(img-8tt4Xp9Y-1692756514197)]
接下來(lái) , 我們?nèi)?UserMapper.xml 里面編寫 SQL 語(yǔ)句
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- "" 里面需要寫接口的路徑-->
<mapper namespace="com.example.demo.mapper.UserMapper">
<select id="getUserByName" resultType="com.example.demo.model.UserInfo">
select * from userinfo where username = #{username}
</select>
</mapper>
接下來(lái) , 我們?nèi)?UserMapper 里面去生成對(duì)應(yīng)的單元測(cè)試
[外鏈圖片轉(zhuǎn)存中…(img-2clTLWJL-1692756514197)]
[外鏈圖片轉(zhuǎn)存中…(img-8aSTt11x-1692756514198)]
[外鏈圖片轉(zhuǎn)存中…(img-RA34kBzR-1692756514199)]
package com.example.demo.mapper;
import com.example.demo.model.UserInfo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest // 當(dāng)前測(cè)試的上下文環(huán)境為 Spring Boot
class UserMapperTest {
// 要測(cè)試誰(shuí) , 就注入誰(shuí)
@Autowired
private UserMapper userMapper;
@Test
void getUserByName() {
UserInfo userInfo = userMapper.getUserByName("admin");
System.out.println(userInfo);
}
}
[外鏈圖片轉(zhuǎn)存中…(img-NCfrBRLt-1692756514199)]
那我們把 #{} 改成 ${} 試試看
就報(bào)錯(cuò)了
[外鏈圖片轉(zhuǎn)存中…(img-plzsJeYu-1692756514200)]
那我們回到 MySQL 里面 , 嘗試去搜索 admin 這個(gè)用戶 , 能夠正常搜索到 .
select * from userinfo where name = 'admin';
[外鏈圖片轉(zhuǎn)存中…(img-ludyzF1G-1692756514201)]
但是我們把 admin 外面的單引號(hào)去掉之后就會(huì)報(bào)錯(cuò)
[外鏈圖片轉(zhuǎn)存中…(img-ngbM0Sde-1692756514201)]
而且這個(gè)報(bào)錯(cuò)信息跟我們程序中的報(bào)錯(cuò)信息一致
[外鏈圖片轉(zhuǎn)存中…(img-ZGEh2hF7-1692756514201)]
我們就可以把 ${} 當(dāng)成這樣的 SQL 語(yǔ)句
select * from userinfo where username = admin; -- 注意:這個(gè) admin 沒(méi)加單引號(hào)
這樣的話 , MySQL 就會(huì)誤以為是 username 字段與 admin 字段進(jìn)行比較 , 但是并未找到 admin 字段 , 所以就報(bào)錯(cuò)了 .
我們之前實(shí)驗(yàn) int 類型的數(shù)據(jù)之所以沒(méi)錯(cuò) , 是因?yàn)槲覀兊?SQL 語(yǔ)句被替換成了這樣 :
select * from userinfo where username = 1; -- 這樣寫, MySQL 就會(huì)以為是判斷 username 是否等于1 , 就不會(huì)報(bào)錯(cuò)了
所以這個(gè)就是 #{} 和 ${} 之間的區(qū)別
#{} : 預(yù)編譯進(jìn)行處理
與之前講的 JDBC 里面的 ? 類似 , #{} 就是事先占一個(gè)位置的 , 這個(gè)位置有可能是 int 類型的 , 也有可能是 String 類型的 , 占了這個(gè)位置 , 這一列是什么 , 預(yù)編譯的這個(gè)位置類型就已經(jīng)確定 , 在執(zhí)行的時(shí)候就不會(huì)發(fā)生報(bào)錯(cuò)了 .
e.g. 假如說(shuō)我們現(xiàn)在的 SQL 語(yǔ)句長(zhǎng)這樣
select * from user where username = ?;
我們要往 ? 里面填充數(shù)據(jù) , 但是我們提前知道 ? 這個(gè)位置是 String 類型 , 所以在對(duì)比的時(shí)候不會(huì)發(fā)生問(wèn)題的 , 就相當(dāng)于公司聚會(huì) , 馬云爸爸的位置還有馬哥的位置為了避免尷尬 , 提前就規(guī)定好了座位 .
[外鏈圖片轉(zhuǎn)存中…(img-k2Xfav5E-1692756514202)]
${} : 字符直接替換
屬于那種小公司 , 誰(shuí)先來(lái)誰(shuí)先找座 , 不講究那些亂碼七遭的說(shuō)頭
小結(jié)一下 :
#{} 就相當(dāng)于自己能分辨數(shù)據(jù)類型 , 比如 String 類型的數(shù)據(jù) , #{} 自己就知道加單引號(hào)
而 ${} 不是 , ${} 就相當(dāng)于一個(gè)空位置 , 誰(shuí)來(lái)誰(shuí)上不添加任何修飾
所以我們傳過(guò)去 String 類型的數(shù)據(jù)后
#{} 就知道把數(shù)據(jù)用單引號(hào)括起來(lái)表示這是字符串
select * from userinfo where username = 'admin'; -- 單引號(hào)是 #{} 主動(dòng)去添加的
${} 就是什么也不做
select * from userinfo where username = admin; -- ${} 不會(huì)進(jìn)行任何修飾
我們要是想讓 ${} 的方式成功 , 我們需要自己去添加單引號(hào)
[外鏈圖片轉(zhuǎn)存中…(img-B35wR1Ks-1692756514202)]
? 那我們以后所有的查詢都用單引號(hào)包裹起來(lái)不就得了 ?
? 是不可以的 . 雖然通過(guò)這種方式也能夠查詢到數(shù)據(jù)
[外鏈圖片轉(zhuǎn)存中…(img-u8YX41bO-1692756514202)]
但是這種查詢是發(fā)生了隱式類型轉(zhuǎn)換的 , 這種方式就不能去走索引了 , 性能就會(huì)大大降低 .
目前是數(shù)據(jù)量少 , 如果我們?cè)诰€上環(huán)境 , 執(zhí)行速度慢的就會(huì)非常明顯了 .
但是剛才我們說(shuō)
還有
0.01
{} 還有 0.01% 的情況 , 肯定還是有它自己存在的價(jià)值的 , 它的價(jià)值就在于使用直接替換 (
還有0.01{}) 的這種形式可以 傳遞/執(zhí)行 MySQL 的關(guān)鍵字
舉個(gè)栗子 :
排序的時(shí)候 , 有兩種情況 , 價(jià)格從低到高 / 價(jià)格從高到低 , 這是一種規(guī)則 .
? 那我們點(diǎn)這里的時(shí)候 , 傳過(guò)去的數(shù)據(jù)是什么 ?
? 傳過(guò)去的就是排序的規(guī)則 , 排序的規(guī)則屬于 MySQL 的關(guān)鍵字 , 在 MySQL 中 , 排序的語(yǔ)句如下圖
[外鏈圖片轉(zhuǎn)存中…(img-6MH6dt2I-1692756514203)]
那我們點(diǎn)擊價(jià)格從高到底 , 傳過(guò)去的就是 desc 關(guān)鍵字 .
點(diǎn)擊價(jià)格從低到高 , 傳過(guò)去的就是 asc 關(guān)鍵字 .
我們自己來(lái)設(shè)計(jì)一個(gè)例子 :
在 UserMapper 里面寫一個(gè)方法聲明 : 查詢所有的信息(按價(jià)格排序)
package com.example.demo.mapper;
import com.example.demo.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
// MyBatis 接口,此注解一定不能省略
@Mapper
public interface UserMapper {
// 按排序條件查詢所有的信息
public List<UserInfo> getAllByOrder(@Param("order") String order);
}
[外鏈圖片轉(zhuǎn)存中…(img-zJRkuXTI-1692756514203)]
接下來(lái) , 我們?nèi)?UserMapper.xml 中編寫 SQL 語(yǔ)句
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- "" 里面需要寫接口的路徑-->
<mapper namespace="com.example.demo.mapper.UserMapper">
<select id="getAllByOrder" resultType="com.example.demo.model.UserInfo">
select * from userinfo order by id ${}
</select>
</mapper>
我們?nèi)ゾ帉憜卧獪y(cè)試
[外鏈圖片轉(zhuǎn)存中…(img-nV5s7fx3-1692756514203)]
[外鏈圖片轉(zhuǎn)存中…(img-9TdQgnl8-1692756514203)]
[外鏈圖片轉(zhuǎn)存中…(img-z92ZTKgI-1692756514204)]
為了更方便查看效果 , 我們?cè)?MySQL 中新添加一條數(shù)據(jù)
insert into userinfo (username,password) values ('zhangsan','zhangsan');
[外鏈圖片轉(zhuǎn)存中…(img-SgzoBkdm-1692756514204)]
如果我們使用的是 asc , 或者是 desc , 我們現(xiàn)在數(shù)據(jù)庫(kù)里面查看一下效果
接下來(lái)我們編寫單元測(cè)試的代碼
package com.example.demo.mapper;
import com.example.demo.model.UserInfo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest // 當(dāng)前測(cè)試的上下文環(huán)境為 Spring Boot
class UserMapperTest {
// 要測(cè)試誰(shuí) , 就注入誰(shuí)
@Autowired
private UserMapper userMapper;
@Test
void getAllByOrder() {
List<UserInfo> list = userMapper.getAllByOrder("asc");
for (UserInfo userInfo : list) {
System.out.println(userInfo.toString());
}
}
}
運(yùn)行一下 :
這是因?yàn)槲覀兊?#{} 把 SQL 語(yǔ)句優(yōu)化成了這樣
select * from userinfo order by id 'asc';
[外鏈圖片轉(zhuǎn)存中…(img-OltZ6jCM-1692756514205)]
這條 SQL 語(yǔ)句是存在語(yǔ)法問(wèn)題的 , 所以會(huì)報(bào)錯(cuò) .
那我們把 #{} 改成 ${} 試試看 , 就可以了
[外鏈圖片轉(zhuǎn)存中…(img-hdUkl4uC-1692756514205)]
[外鏈圖片轉(zhuǎn)存中…(img-wvWLxNaC-1692756514205)]
7.1.2 SQL 注入問(wèn)題
假如我們有一個(gè)這樣的例子 :
正常的查詢應(yīng)該是這樣 :
那么什么叫 SQL 注入呢 ?
SQL 注入就是在不知道賬號(hào)密碼的情況下 , 就訪問(wèn)到了你的信息
SQL 注入就是這樣的一個(gè)簡(jiǎn)單字符串 : ' or 1='1'
他利用的就是 SQL 執(zhí)行漏洞
直接來(lái)看運(yùn)行結(jié)果
[外鏈圖片轉(zhuǎn)存中…(img-9RPF7has-1692756514206)]
’ or 1=‘1’ 并不是你真正的密碼啊 , 怎么就獲取到你的信息了呢 ?
[外鏈圖片轉(zhuǎn)存中…(img-fnKbtTKf-1692756514206)]
如果使用了 ${} , 那么程序就會(huì)存在 SQL 注入的問(wèn)題 , 因?yàn)樗麜?huì)把 ’ or 1=‘1’ 當(dāng)成關(guān)鍵字來(lái)去執(zhí)行代碼
而 #{} 不會(huì)存在這種問(wèn)題 , #{} 是預(yù)處理的 , 他會(huì)把 ’ or 1=‘1’ 當(dāng)成密碼來(lái)看 , 不認(rèn)為這一串是 SQL 語(yǔ)句
那我們寫個(gè)代碼來(lái)實(shí)驗(yàn)一下
我們?cè)?UserMapper 里面寫一個(gè)登錄方法
package com.example.demo.mapper;
import com.example.demo.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
// MyBatis 接口,此注解一定不能省略
@Mapper
public interface UserMapper {
public UserInfo login(@Param("username") String username,
@Param("password") String password);
}
接下來(lái) , 我們?cè)?UserMapper.xml 里面編寫 SQL 語(yǔ)句
注意 : 這時(shí)候要保證我們數(shù)據(jù)表里面僅有一條數(shù)據(jù) , 不然會(huì)報(bào)錯(cuò)
因?yàn)槲覀兎祷刂凳欠祷氐膶?duì)象 , 而不是 List
[外鏈圖片轉(zhuǎn)存中…(img-gs7buBy2-1692756514206)]
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- "" 里面需要寫接口的路徑-->
<mapper namespace="com.example.demo.mapper.UserMapper">
<select id="login" resultType="com.example.demo.model.UserInfo">
select * from userinfo where username = ${username} and password = ${password}
</select>
</mapper>
接下來(lái) , 我們編寫單元測(cè)試
[外鏈圖片轉(zhuǎn)存中…(img-QZ7RbhER-1692756514206)]
package com.example.demo.mapper;
import com.example.demo.model.UserInfo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest // 當(dāng)前測(cè)試的上下文環(huán)境為 Spring Boot
class UserMapperTest {
// 要測(cè)試誰(shuí) , 就注入誰(shuí)
@Autowired
private UserMapper userMapper;
@Test
void login() {
// 測(cè)試 ${} 的時(shí)候 , 要把賬號(hào)用單引號(hào)括起來(lái)
// 因?yàn)?${} 識(shí)別字符串的時(shí)候會(huì)把他當(dāng)成 SQL 語(yǔ)句,然后報(bào)錯(cuò)
String username = "'admin'";
String password = "'' or 1='1'";//SQL 注入
UserInfo userInfo = userMapper.login(username, password);
System.out.println("userinfo ->" + userInfo);
}
}
[外鏈圖片轉(zhuǎn)存中…(img-sOyG1T5v-1692756514207)]
還可以這樣寫
再測(cè)試 #{}
package com.example.demo.mapper;
import com.example.demo.model.UserInfo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest // 當(dāng)前測(cè)試的上下文環(huán)境為 Spring Boot
class UserMapperTest {
// 要測(cè)試誰(shuí) , 就注入誰(shuí)
@Autowired
private UserMapper userMapper;
@Test
void login() {
// 測(cè)試 #{} 的時(shí)候,就不用再加單引號(hào)了
// 因?yàn)?#{} 會(huì)自動(dòng)幫我們加
String username = "admin";
String password = "' or 1='1'";//SQL 注入
UserInfo userInfo = userMapper.login(username, password);
System.out.println("userinfo ->" + userInfo);
}
}
7.1.3 模糊查詢 : like 查詢
對(duì)于普通的查詢 , 我們使用 #{} 都能搞得定 , 但是模糊查詢比較特殊 , 模糊查詢使用 % 或者 _ 來(lái)指代 0-n 個(gè)字符
本來(lái)我們的 SQL 實(shí)現(xiàn)是這個(gè)樣子
<select id="findUserByName2" resultType="com.example.demo.model.UserInfo">
select * from userinfo where username like '%#{username}%';
</select>
我們上面使用的是 #{} 的形式 , 他會(huì)自己判斷類型 . 我們這里傳輸過(guò)去的是 String 類型 , 所以實(shí)際的 SQL 就變?yōu)榱?/p>
select * from userinfo where username like '%'username'%';
-- username 是字符串類型 , 就自動(dòng)在 username 兩邊加了單引號(hào)
這樣肯定是不可以的
[外鏈圖片轉(zhuǎn)存中…(img-AyM3fFuh-1692756514208)]
但是我們想要實(shí)現(xiàn)的 SQL 是 :
select * from userinfo where username like '%m%';
那咱們就改成使用 ${} 不就可以了嗎 ? 答案真的有這樣簡(jiǎn)單嗎 ?
其實(shí)是不行的 . 想想之前咱們的淘寶案例 , 傳過(guò)去的是 asc desc 這兩個(gè)關(guān)鍵字 , 這兩個(gè)關(guān)鍵字是可以進(jìn)行窮舉的 , 意思就是我們可以寫兩條 SQL 語(yǔ)句塊來(lái)實(shí)現(xiàn)升序排列以及降序排列 , 然后這兩個(gè)關(guān)鍵字我們是在 Controller 層里面去進(jìn)行參數(shù)校驗(yàn)的 , 參數(shù)校驗(yàn)成功才可以去實(shí)現(xiàn)相對(duì)應(yīng)的功能 .但是用戶名能窮舉嗎 , 他的可能性無(wú)窮無(wú)盡 , 那咱們還能寫無(wú)數(shù)個(gè) SQL 語(yǔ)句塊 ?
這肯定是不行的 , 不過(guò)我們可以通過(guò) SQL 里面的一個(gè)內(nèi)置函數(shù) concat() 來(lái)進(jìn)行處理 ,他可以實(shí)現(xiàn)將多個(gè)字符拼接在一起 ,那我們就把 %#{username}#
分成了三個(gè)部分 : % 、#{username} 、# ,這樣的話把他們?nèi)齻€(gè)拼接到一起就可以了
<select id="findUserByName3" resultType="com.example.demo.model.UserInfo">
select * from userinfo where username like concat('%',#{username},'%')
</select>
那我們來(lái)試驗(yàn)一下 , 先在 UserMapper 里面寫方法聲明
package com.example.demo.mapper;
import com.example.demo.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
// MyBatis 接口,此注解一定不能省略
@Mapper
public interface UserMapper {
// 模糊查詢
public UserInfo findUserByName3(@Param("username") String username);
}
然后 UserMapper.xml 我們剛才已經(jīng)寫完了
接下來(lái)完成單元測(cè)試即可
[外鏈圖片轉(zhuǎn)存中…(img-2514weQ0-1692756514208)]
[外鏈圖片轉(zhuǎn)存中…(img-juKFtIJ1-1692756514208)]
[外鏈圖片轉(zhuǎn)存中…(img-kvWM2XxK-1692756514209)]
package com.example.demo.mapper;
import com.example.demo.model.UserInfo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest // 當(dāng)前測(cè)試的上下文環(huán)境為 Spring Boot
class UserMapperTest {
// 要測(cè)試誰(shuí) , 就注入誰(shuí)
@Autowired
private UserMapper userMapper;
@Test
void findUserByName3() {
UserInfo userInfo = userMapper.findUserByName3("m");//傳過(guò)去要按哪個(gè)字符/字符串去查找
System.out.println(userInfo.toString());
}
}
[外鏈圖片轉(zhuǎn)存中…(img-wSRirPM6-1692756514209)]
用 MySQL 命令行也查到了相關(guān)信息
[外鏈圖片轉(zhuǎn)存中…(img-ymdkh1Li-1692756514209)]
7.2 多表查詢
7.2.1 返回類型 : resultType VS resultMap
我們?cè)谶M(jìn)入公司上班的過(guò)程中 , 肯定會(huì)遇見(jiàn)這樣的問(wèn)題 :
之前我們學(xué)過(guò) , 數(shù)據(jù)庫(kù)的字段名一般通過(guò)下劃線來(lái)分隔 , 比如 user_name
而 Java 語(yǔ)言的規(guī)范是變量的命名使用小駝峰命名法 , 比如 userName
那在公司之后 , 操作數(shù)據(jù)庫(kù)的是一波人 , 他們習(xí)慣變量使用下劃線分隔
那為了與數(shù)據(jù)庫(kù)中的字段名保持一致 , 我們 Java 程序員就不得不低頭 , 也采用下劃線分割的方式 ?
那肯定不行 , 但是不保持一致還查詢不到
[外鏈圖片轉(zhuǎn)存中…(img-dGLjjAiN-1692756514209)]那我們 MyBatis 也考慮到了這個(gè)問(wèn)題 , 就推出了 resultMap , 他的功能與 resultType 一致
resultMap 可以解決程序中實(shí)體類的名稱和數(shù)據(jù)庫(kù)當(dāng)中字段名不一樣的問(wèn)題
那 resultMap 怎樣去使用呢 ?
resultMap 使用起來(lái)相對(duì)麻煩一下 , 他需要在 xml 文件中進(jìn)行配置
我們就可以使用一個(gè) 標(biāo)簽在 xxxMapper.xml 里面進(jìn)行聲明
<resultMap id="BaseMap" type="com.example.demo.model.User">
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="password" property="pwd"></result>
</resultMap>
這段代碼就相當(dāng)于我們定義了一個(gè)返回結(jié)果的字典來(lái)映射數(shù)據(jù)庫(kù)當(dāng)中的字段和實(shí)體類當(dāng)中的屬性<id column="id" property="id"></id>
意思就是把程序中的 id 和數(shù)據(jù)庫(kù)中的 id 匹配上 , 其中 : property 代表的是程序里面的屬性名 , column 代表數(shù)據(jù)庫(kù)里面的字段<result column="username" property="username"></result>
、<result column="password" property="pwd"></result>
就是把 username 和 password 匹配上
那我們就可以在 UserMapper.xml 里面去聲明 resultMap 了
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 里面需要寫接口的路徑-->
<mapper namespace="com.example.demo.mapper.UserMapper">
<!-- id 的名字無(wú)所謂,甚至可以重復(fù) -->
<!--
type 代表要映射的哪個(gè)類
參數(shù)仍然是包名+類名
-->
<resultMap id="BaseMap" type="com.example.demo.model.UserInfo">
<!--
里面要寫需要映射的字段
column 代表數(shù)據(jù)庫(kù)中的字段
property 代表實(shí)體類當(dāng)中的屬性
-->
<!-- 推薦把所有情況都寫上,即使字段名與屬性名是一致的 -->
<id column="id" property="id"></id> <!-- 這種情況是設(shè)置主鍵 -->
<!-- 接下來(lái)其他的設(shè)置成普通類型的即可 -->
<result column="username" property="name"></result>
<result column="password" property="password"></result>
<result column="photo" property="photo"></result>
<result column="createtime" property="createtime"></result>
<result column="updatetime" property="updatetime"></result>
<result column="state" property="state"></result>
</resultMap>
<select id="getAll" resultType="com.example.demo.model.UserInfo">
select * from userInfo
</select>
</mapper>
[外鏈圖片轉(zhuǎn)存中…(img-q2nbdvBl-1692756514210)]
然后我們測(cè)試的是 getAll 方法 ,就需要把 getAll 的方法對(duì)應(yīng)的 xml 實(shí)現(xiàn)里面的 ResultType 刪除掉 ,改成 ResultMap 即可 , 參數(shù)填寫上面的 id
[外鏈圖片轉(zhuǎn)存中…(img-Oj8RyQxC-1692756514210)]
接下來(lái) , 我們就可以去執(zhí)行單元測(cè)試了
[外鏈圖片轉(zhuǎn)存中…(img-S95mEhYZ-1692756514210)]
雖然這樣寫麻煩一些 , 但是他可以解決數(shù)據(jù)庫(kù)字段名與實(shí)體類屬性名不一致的問(wèn)題
[外鏈圖片轉(zhuǎn)存中…(img-msA7kh6F-1692756514210)]
7.2.2 多表查詢
我們的數(shù)據(jù)庫(kù)目前有兩張表 : articleinfo 以及 userinfo
其實(shí)這兩張表是有關(guān)聯(lián)關(guān)系的
[外鏈圖片轉(zhuǎn)存中…(img-nDlRdeb4-1692756514211)]
articleInfo 里面的 uid 對(duì)應(yīng)的就是 userinfo 里面的 id , 這其實(shí)就是一個(gè)外鍵 .
那如果我們想得到所有文章的信息 ,并且得到每篇文章對(duì)應(yīng)的作者的時(shí)候 , 我們就需要采用多表查詢的方式了
在 MyBatis 里面 , 多表查詢?cè)鯓訉?shí)現(xiàn)呢 ?
目前我們的需求就是打印文章表的時(shí)候 , 順便打印作者姓名
所以我們就可以先在文章表對(duì)應(yīng)的實(shí)體類上 , 先添加作者姓名屬性
[外鏈圖片轉(zhuǎn)存中…(img-4eCkQ1Lj-1692756514211)]
package com.example.demo.model;
import lombok.Data;
import java.util.Date;
/**
* 文章的實(shí)體類
*/
@Data
public class ArticleInfo {
private int id;
private String title;
private String content;
private Date createtime;
private Date updatetime;
private int uid;
private int rcount;//訪問(wèn)量
private int state;//狀態(tài)
// 多表查詢
private String username;//文章作者名 - 來(lái)源于別的表
}
接下來(lái)在 ArticleMapper 里面寫多表聯(lián)合查詢的方法聲明
package com.example.demo.mapper;
import com.example.demo.model.ArticleInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
// 記得添加 @Mapper 注解
@Mapper
public interface ArticleInfoMapper {
// 查詢文章及其作者信息
public List<ArticleInfo> getAll();
}
[外鏈圖片轉(zhuǎn)存中…(img-mPfL4vvi-1692756514211)]
接下來(lái)去 ArticleMapper.xml 里面編寫多表聯(lián)合查詢的 SQL 語(yǔ)句
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- "" 里面需要寫接口的路徑-->
<mapper namespace="com.example.demo.mapper.ArticleInfoMapper">
<select id="getAll" resultType="com.example.demo.model.ArticleInfo">
select a.*,u.username from articleinfo as a
left join userinfo as u
on a.uid = u.id;
</select>
</mapper>
[外鏈圖片轉(zhuǎn)存中…(img-L3I5fYB7-1692756514211)]
先去數(shù)據(jù)庫(kù)中看一下我們的 SQL 語(yǔ)句有沒(méi)有問(wèn)題
沒(méi)有問(wèn)題我們就可以跑單元測(cè)試了
[外鏈圖片轉(zhuǎn)存中…(img-pPmbenXq-1692756514212)]
[外鏈圖片轉(zhuǎn)存中…(img-MYhMRECU-1692756514212)]
[外鏈圖片轉(zhuǎn)存中…(img-D9tMXYsy-1692756514212)]
package com.example.demo.mapper;
import com.example.demo.model.ArticleInfo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
// 要記得加這個(gè)注解,代表 Spring Boot 的單元測(cè)試
@SpringBootTest
class ArticleInfoMapperTest {
// 屬性注入
@Autowired
private ArticleInfoMapper articleInfoMapper;
@Test
void getAll() {
List<ArticleInfo> list = articleInfoMapper.getAll();
for (ArticleInfo info : list) {
System.out.println(info);
}
}
}
也成功打印了
要注意 :
那我們這兩個(gè)字段的名稱就是對(duì)不上呢 ?
我們有兩種方案 :
- 使用 resultMap
我們把 AticleInfo 實(shí)體類里面的 username 屬性改成了 name
在 ArticleMapper.xml 里面寫 resultMap 標(biāo)簽
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- "" 里面需要寫接口的路徑-->
<mapper namespace="com.example.demo.mapper.ArticleInfoMapper">
<!--
id 隨便起,可以重復(fù)
type 代表要映射的哪個(gè)類
參數(shù)仍然是包名+類名
-->
<resultMap id="BaseMap" type="com.example.demo.model.ArticleInfo">
<!-- id 代表主鍵 -->
<id column="id" property="id"></id> <!-- column 代表數(shù)據(jù)庫(kù)字段名,property 代表實(shí)體類屬性名 -->
<!-- result 代表普通屬性 -->
<result column="username" property="name"></result>
<!-- ... -->
<!-- 其他的省略了 -->
</resultMap>
<!-- 使用resultMap 解決名字不一樣問(wèn)題 -->
<!-- 要記得將這里面的 resultType 改成 resultMap -->
<!-- 參數(shù)就是上面寫的 id,也就是 BaseMap -->
<select id="getAll" resultMap="BaseMap">
select a.*,u.username from articleinfo as a
left join userinfo as u
on a.uid = u.id;
</select>
</mapper>
單元測(cè)試運(yùn)行一下
[外鏈圖片轉(zhuǎn)存中…(img-BYVWPDbK-1692756514213)]
- 給要查詢的這一列進(jìn)行重命名 , 重命名成你的屬性名
所以我們只需要在 SQL 語(yǔ)句上進(jìn)行重命名操作 (as 關(guān)鍵字)
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- "" 里面需要寫接口的路徑-->
<mapper namespace="com.example.demo.mapper.ArticleInfoMapper">
<select id="getAll" resultType="com.example.demo.model.ArticleInfo">
select a.*,u.username as name from articleinfo as a
left join userinfo as u
on a.uid = u.id;
</select>
</mapper>
[外鏈圖片轉(zhuǎn)存中…(img-t6x03m4p-1692756514214)]
八 . 動(dòng)態(tài) SQL 使用
這里是官方的 動(dòng)態(tài) SQL 文檔 : https://mybatis.org/mybatis-3/zh/dynamic-sql.html
官方 : 動(dòng)態(tài) SQL 是 MyBatis 的強(qiáng)大特性之一。如果你使用過(guò) JDBC 或其它類似的框架,你應(yīng)該能理解根據(jù)不同條件拼接 SQL 語(yǔ)句有多痛苦,例如拼接時(shí)要確保不能忘記添加必要的空格,還要注意去掉列表最后一個(gè)列名的逗號(hào)。利用動(dòng)態(tài) SQL , 可以徹底擺脫這種痛苦。
8.1 標(biāo)簽
MyBatis 主要就是解決必傳參數(shù)以及非必傳參數(shù)的 , 也就是我們常見(jiàn)的必填項(xiàng)與非必填項(xiàng)
[外鏈圖片轉(zhuǎn)存中…(img-66QuD24A-1692756514214)]
標(biāo)簽 這種情況就適用于 : 傳遞的參數(shù)我現(xiàn)在是不確定的 , 有可能有用 , 有可能沒(méi)用 .
我們就可以使用 標(biāo)簽來(lái)進(jìn)行判斷 , 他就會(huì)根據(jù)傳遞的參數(shù)進(jìn)行 SQL 語(yǔ)句拼接
我們用 photo 字段舉例
photo 用戶有可能傳 ,有可能不傳 .
我們先來(lái)模擬一下不使用動(dòng)態(tài)標(biāo)簽的時(shí)候 , SQL 語(yǔ)句的編寫
insert into userinfo (username,password,photo) values ('張三','zhangsan',null);
-- 在沒(méi)有學(xué)習(xí)動(dòng)態(tài)標(biāo)簽的時(shí)候,我們是把可選參數(shù)和必選參數(shù)一起去添加的
-- 前端沒(méi)給我們傳過(guò)來(lái)頭像數(shù)據(jù),那我們就先插入一個(gè) null
如果我們有動(dòng)態(tài)標(biāo)簽了 , 用戶沒(méi)傳頭像 , 那我們插入數(shù)據(jù)就可以不管頭像字段了
insert into userinfo (username,password) values ('李四','lisi');
[外鏈圖片轉(zhuǎn)存中…(img-1Japvhsq-1692756514216)]
NULL 和 空不是一樣的東西 ,所以事情就變得很難搞了
[外鏈圖片轉(zhuǎn)存中…(img-RbrvXyvn-1692756514217)]
想要實(shí)現(xiàn)必選項(xiàng)的問(wèn)題 , 我們就可以使用 標(biāo)簽
語(yǔ)法 :
<if test="photo!=null">
...
</if>
如果有多個(gè)位置需要?jiǎng)討B(tài)輸入 , 我們就可以寫多個(gè) 標(biāo)簽
我們來(lái)試驗(yàn)一下 , 以 User的添加舉例
在這里再給大家推薦一個(gè)十分好用的插件 : MyBatisX
[外鏈圖片轉(zhuǎn)存中…(img-2WILAZKT-1692756514217)]
[外鏈圖片轉(zhuǎn)存中…(img-VdmfO0Yn-1692756514217)]
這個(gè)插件的作用就是 :
點(diǎn)擊前面的小鳥 , 就可以跳轉(zhuǎn)到接口聲明或者 xxx.xml 實(shí)現(xiàn)
[外鏈圖片轉(zhuǎn)存中…(img-F9riPc0O-1692756514217)]
[外鏈圖片轉(zhuǎn)存中…(img-f4O85Km6-1692756514218)]
我們?cè)?UserMapper 接口里面去聲明方法
package com.example.demo.mapper;
import com.example.demo.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
// MyBatis 接口,此注解一定不能省略
@Mapper
public interface UserMapper {
// 添加用戶
public int add(@Param("username") String username,
@Param("password") String password,
@Param("photo") String photo);
}
[外鏈圖片轉(zhuǎn)存中…(img-HvhdNRiB-1692756514218)]
接下來(lái)我們?nèi)?UserMapper.xml 里面去實(shí)現(xiàn)具體 SQL 實(shí)現(xiàn)
<!-- insert 只有一個(gè)參數(shù):函數(shù)名 -->
<insert id="add">
insert into userinfo(username,password,photo)
values(#{username},#{password},#{photo})
</insert>
但是我們的 photo 字段不是必傳項(xiàng) , 所以需要程序員去設(shè)置一下
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- "" 里面需要寫接口的路徑-->
<mapper namespace="com.example.demo.mapper.UserMapper">
<!-- insert 只有一個(gè)參數(shù):函數(shù)名 -->
<insert id="add">
insert into userinfo (username,
<if test="photo!=null">
photo,
</if>
password)
values (#{username}
<if test="photo!=null">
,#{photo}
</if>
,#{password})
</insert>
</mapper>
添加完之后 , 我們就可以通過(guò)單元測(cè)試驗(yàn)證一下了
如果數(shù)據(jù)庫(kù)中的自增主鍵 ID 已經(jīng)混亂了 , 我們就可以通過(guò)這條 SQL 去修改自增主鍵的 ID
alter table ‘your_table_name’ auto_increment = '期望的id值'
[外鏈圖片轉(zhuǎn)存中…(img-Mf3MK18l-1692756514218)]
[外鏈圖片轉(zhuǎn)存中…(img-03k2Dyjw-1692756514218)]
我們先實(shí)驗(yàn) : 可選參數(shù)傳值的情況
package com.example.demo.mapper;
import com.example.demo.model.UserInfo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest // 當(dāng)前測(cè)試的上下文環(huán)境為 Spring Boot
class UserMapperTest {
// 要測(cè)試誰(shuí) , 就注入誰(shuí)
@Autowired
private UserMapper userMapper;
@Test
void add() {
int result = userMapper.add("王五","wangwu","default.png");
System.out.println("添加用戶結(jié)果:" + result);
}
}
[外鏈圖片轉(zhuǎn)存中…(img-s0470fUN-1692756514219)]
[外鏈圖片轉(zhuǎn)存中…(img-X5u7eIOF-1692756514219)]
我們?cè)賮?lái)試驗(yàn)一下可選參數(shù)不傳值的情況 :
package com.example.demo.mapper;
import com.example.demo.model.UserInfo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest // 當(dāng)前測(cè)試的上下文環(huán)境為 Spring Boot
class UserMapperTest {
// 要測(cè)試誰(shuí) , 就注入誰(shuí)
@Autowired
private UserMapper userMapper;
@Test
void add() {
int result = userMapper.add("老六","laoliu",null);
System.out.println("添加用戶結(jié)果:" + result);
}
}
[外鏈圖片轉(zhuǎn)存中…(img-vIyYPcKo-1692756514219)]
[外鏈圖片轉(zhuǎn)存中…(img-Tpa2bHBh-1692756514220)]
8.2 標(biāo)簽
字面意思就是 : 去掉某些信息
我們就可以結(jié)合 標(biāo)簽完成一些事情
標(biāo)簽中有如下屬性 :
- prefix : 表示整個(gè)語(yǔ)句塊 , 以 prefix 的值作為前綴
- suffix : 表示整個(gè)語(yǔ)句塊 , 以 suffix 的值作為后綴
- prefixOverrides : 表示整個(gè)語(yǔ)句塊要去除掉的前綴
- suffixOverrides : 表示整個(gè)語(yǔ)句塊要去除掉的后綴
以 prefixOverrides、suffixOverrides 我們來(lái)舉個(gè)栗子 :
我們剛才在寫這里的時(shí)候 , 其實(shí)有點(diǎn)取巧了
[外鏈圖片轉(zhuǎn)存中…(img-WsjamgOd-1692756514220)]
如果我們這樣寫
<insert id="add">
insert into userinfo (username,password,
<if test="photo!=null">
photo
</if>)
values (#{username},#{password},
<if test="photo!=null">
#{photo}
</if>
)
</insert>
如果我們傳入照片 , 無(wú)所謂了
但是如果我們沒(méi)傳進(jìn)去照片 , 那么 SQL 語(yǔ)句就變成了
insert into userinfo (username,password, ) values ("老六","laoliu", );
[外鏈圖片轉(zhuǎn)存中…(img-6MxS5rbI-1692756514220)]
就變成了病句 , 最后面的逗號(hào)就單蹦了 .
那使用我們的 就解決不了了 , 我們就可以搭配 使用
我們把場(chǎng)景設(shè)置的極端一些 : 所有字段都設(shè)置成可填字段
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- "" 里面需要寫接口的路徑-->
<mapper namespace="com.example.demo.mapper.UserMapper">
<!-- insert 只有一個(gè)參數(shù):函數(shù)名 -->
<insert id="add">
insert into userinfo
<!--
prefix 代表前綴,在這里加 prefix="(" , 就不用寫 SQL 里面前面的"("了
suffix 代表后綴,在這里加 suffix=")" , 就不用寫 SQL 里面后面的")"了
suffixOverrides 表示整個(gè)語(yǔ)句塊要去除掉的后綴,意思是我們會(huì)把SQL中最后面的逗號(hào)去除掉,這樣就不會(huì)有錯(cuò)了
-->
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="username!=null">
username,
</if>
<if test="password!=null">
password,
</if>
<if test="photo!=null">
photo,
</if>
</trim>
values
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="username!=null">
#{username},
</if>
<if test="password!=null">
#{password},
</if>
<if test="photo!=null">
#{photo},
</if>
</trim>
</insert>
</mapper>
其中 , 我們最后一個(gè)字段 photo 加不加逗號(hào)無(wú)所謂了 , 因?yàn)槲覀円呀?jīng)有 標(biāo)簽里面的 suffixOverrides 屬性保駕護(hù)航了 , 不加也不會(huì)報(bào)錯(cuò) , 加了更不會(huì)報(bào)錯(cuò)
測(cè)試一下吧
先測(cè)試每個(gè)字段都插入的情況
package com.example.demo.mapper;
import com.example.demo.model.UserInfo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest // 當(dāng)前測(cè)試的上下文環(huán)境為 Spring Boot
class UserMapperTest {
// 要測(cè)試誰(shuí) , 就注入誰(shuí)
@Autowired
private UserMapper userMapper;
@Test
void add() {
int result = userMapper.add("老七","laoqi","default.png");
System.out.println("添加用戶結(jié)果:" + result);
}
}
[外鏈圖片轉(zhuǎn)存中…(img-dSKxgy0Z-1692756514220)]
[外鏈圖片轉(zhuǎn)存中…(img-odbTSjoC-1692756514221)]
我們?cè)賮?lái)試一下不傳入 photo 字段的情況
package com.example.demo.mapper;
import com.example.demo.model.UserInfo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest // 當(dāng)前測(cè)試的上下文環(huán)境為 Spring Boot
class UserMapperTest {
// 要測(cè)試誰(shuí) , 就注入誰(shuí)
@Autowired
private UserMapper userMapper;
@Test
void add() {
int result = userMapper.add("老八","laoba",null);
System.out.println("添加用戶結(jié)果:" + result);
}
}
[外鏈圖片轉(zhuǎn)存中…(img-PZMHuJDc-1692756514221)]
[外鏈圖片轉(zhuǎn)存中…(img-wvYvaqkW-1692756514221)]
8.3 標(biāo)簽
標(biāo)簽是針對(duì)于查詢的
比如一個(gè)頁(yè)面上 , 我們可以通過(guò)用戶名查詢 , 也可以通過(guò)用戶 ID 去查詢
我們選擇使用用戶名查詢 , 那么這個(gè)用戶名我們可以傳 , 也可以不傳 .
傳的話 , 就返回對(duì)應(yīng)用戶信息 . 不傳的話 , username 就是空 , 應(yīng)該返回所有用戶信息 , 但是事與愿違
[外鏈圖片轉(zhuǎn)存中…(img-942M8mAE-1692756514221)]
那我們能不能使用 標(biāo)簽?zāi)??
正常情況下 , 我們使用 標(biāo)簽 , 就可以把 SQL 看成這樣
select * from userinfo where username = '';
但是我們不傳參數(shù)的話 , SQL 就變成了這樣
select * from userinfo where;
這就有錯(cuò)誤了
再比如說(shuō) :
select * from userinfo where username = 'zhangsan' and photo = '';
假如我們使用 標(biāo)簽 , 把 where username = 'zhangsan'
包裹起來(lái) , 再用另外一個(gè) 標(biāo)簽把 and photo = ''
包裹起來(lái) , 那我們第一個(gè)參數(shù)不填 , SQL 就變成了這樣
select * from userinfo and photo = '';
這個(gè) SQL 也是有錯(cuò)誤的
所以 標(biāo)簽就不太好使了 , 我們需要使用 標(biāo)簽了 , 標(biāo)簽仍然是與 標(biāo)簽搭配使用
我們把之前的 getUserByName 改造一下
[外鏈圖片轉(zhuǎn)存中…(img-Sjcoz30u-1692756514222)]
提前把這里改回來(lái)
[外鏈圖片轉(zhuǎn)存中…(img-Am1mZEsv-1692756514222)]
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- "" 里面需要寫接口的路徑-->
<mapper namespace="com.example.demo.mapper.UserMapper">
<select id="getUserByName" resultType="com.example.demo.model.UserInfo">
select * from userinfo
<where>
<!-- <where>標(biāo)簽可以天然去除第一個(gè)標(biāo)簽里面的 and -->
<if test="username!=null">
and username=#{username}
</if>
</where>
</select>
</mapper>
進(jìn)行單元測(cè)試
package com.example.demo.mapper;
import com.example.demo.model.UserInfo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest // 當(dāng)前測(cè)試的上下文環(huán)境為 Spring Boot
class UserMapperTest {
// 要測(cè)試誰(shuí) , 就注入誰(shuí)
@Autowired
private UserMapper userMapper;
@Test
void getUserByName() {
UserInfo userInfo = userMapper.getUserByName("admin");
System.out.println(userInfo);
}
}
[外鏈圖片轉(zhuǎn)存中…(img-h4aRIydG-1692756514222)]
我們?cè)囈幌虏粋髅?/p>
package com.example.demo.mapper;
import com.example.demo.model.UserInfo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest // 當(dāng)前測(cè)試的上下文環(huán)境為 Spring Boot
class UserMapperTest {
// 要測(cè)試誰(shuí) , 就注入誰(shuí)
@Autowired
private UserMapper userMapper;
@Test
void getUserByName() {
UserInfo userInfo = userMapper.getUserByName(null);
System.out.println(userInfo);
}
}
清除一下無(wú)效數(shù)據(jù)
因?yàn)闇y(cè)試類返回的是 UserInfo , 而不是 UserInfo 數(shù)組 , 沒(méi)能力支持多條數(shù)據(jù) , 只能實(shí)驗(yàn)一組數(shù)據(jù)
[外鏈圖片轉(zhuǎn)存中…(img-8MLtpTWv-1692756514223)]
運(yùn)行一下
[外鏈圖片轉(zhuǎn)存中…(img-IIhH9qMk-1692756514223)]
我們就可以對(duì) 標(biāo)簽做一個(gè)總結(jié) :
- 根據(jù) 標(biāo)簽里面的內(nèi)容 , 決定要不要拼接 where 語(yǔ)句
- 標(biāo)簽會(huì)去除掉最前面的 and 關(guān)鍵字 , 讓 SQL 符合數(shù)據(jù)庫(kù)的執(zhí)行標(biāo)準(zhǔn)
[外鏈圖片轉(zhuǎn)存中…(img-xf9jzGfc-1692756514223)]
- 標(biāo)簽 等價(jià)于
<trim prefix="where" prefixOverrides="and">
8.4 標(biāo)簽
標(biāo)簽常常使用在 update 語(yǔ)句中
update userinfo set password = "123456" where id = 1;
標(biāo)簽是去除掉后面的 ,
的 , 而 標(biāo)簽是去除掉前面的 and
的
我們可以來(lái)試一試
在 UserMapper 接口當(dāng)中聲明方法
package com.example.demo.mapper;
import com.example.demo.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
// MyBatis 接口,此注解一定不能省略
@Mapper
public interface UserMapper {
// 修改用戶
public int updateById(@Param("username") String username,
@Param("password") String password,
@Param("id") Integer id);
}
[外鏈圖片轉(zhuǎn)存中…(img-ycBNpq6A-1692756514224)]
然后去 UserMapper.xml 中實(shí)現(xiàn)具體的 SQL
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- "" 里面需要寫接口的路徑-->
<mapper namespace="com.example.demo.mapper.UserMapper">
<update id="updateById">
update userinfo
<set>
<if test="username != null">
username = #{username},
</if>
<if test="password != null">
password = #{password},
</if>
</set>
where id=#{id}
</update>
</mapper>
[外鏈圖片轉(zhuǎn)存中…(img-czGu6trh-1692756514224)]
接下來(lái) , 編寫單元測(cè)試代碼
單元測(cè)試的代碼生成方法就不一一截圖了
package com.example.demo.mapper;
import com.example.demo.model.UserInfo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest // 當(dāng)前測(cè)試的上下文環(huán)境為 Spring Boot
class UserMapperTest {
// 要測(cè)試誰(shuí) , 就注入誰(shuí)
@Autowired
private UserMapper userMapper;
@Test
void updateById() {
int result = userMapper.updateById(null,"666",1);
System.out.println("添加用戶結(jié)果:" + result);
}
}
[外鏈圖片轉(zhuǎn)存中…(img-ZlXCiNv3-1692756514224)]
標(biāo)簽的作用 :
- 生成 set 關(guān)鍵字
- 去除最后一個(gè)
,
8.5 標(biāo)簽
的用途 : 我想實(shí)現(xiàn)一個(gè)批量刪除的功能 , 傳遞的是一個(gè) id 集合
delete from userinfo where id in(3,4,5);
但是我們括號(hào)里面并不是幾個(gè)數(shù) , 而是一個(gè) list 集合 , 我們通過(guò) 標(biāo)簽循環(huán)集合當(dāng)中的每個(gè)元素
標(biāo)簽有幾個(gè)屬性
- collection : 綁定方法參數(shù)中的集合(綁定的參數(shù)名 <- 接口方法中集合的名字) , 如 List,Set,Map 或數(shù)組對(duì)象
- item : 遍歷時(shí)的每?個(gè)對(duì)象
- open : 語(yǔ)句塊開頭的字符串 ( 類似 的 prefix )
- close : 語(yǔ)句塊結(jié)束的字符串 ( 類似 的 suffix )
- separator : 每次遍歷之間間隔的字符串
[外鏈圖片轉(zhuǎn)存中…(img-wlwAM7Q5-1692756514225)]
為了測(cè)試 標(biāo)簽 , 我們?cè)跀?shù)據(jù)庫(kù)中構(gòu)造幾條數(shù)據(jù)
insert into userinfo (username,password) values ('張三','123456');
insert into userinfo (username,password) values ('李四','123456');
insert into userinfo (username,password) values ('王五','123456');
[外鏈圖片轉(zhuǎn)存中…(img-7ntvwiGU-1692756514225)]
先在 UserMapper 接口中聲明方法
package com.example.demo.mapper;
import com.example.demo.model.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
// MyBatis 接口,此注解一定不能省略
@Mapper
public interface UserMapper {
// 刪除多條數(shù)據(jù)
public int delByIds(List<Integer> ids);
}
再去 UserMapper.xml 中實(shí)現(xiàn) 具體 SQL
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- "" 里面需要寫接口的路徑-->
<mapper namespace="com.example.demo.mapper.UserMapper">
<delete id="delByIds">
delete from userinfo where id in
<!--
collection:UserMapper接口當(dāng)中集合類的名稱
item:每個(gè)元素的屬性,名字無(wú)所謂,item="item"引號(hào)里的內(nèi)容要和#{item}中{}里面的內(nèi)容相匹配
open:前置
close:后置
separator:分隔符
-->
<foreach collection="ids" item="item" open="(" close=")" separator=",">
#{item}
</foreach>
</delete>
</mapper>
[外鏈圖片轉(zhuǎn)存中…(img-oXSszqX0-1692756514226)]
最后生成單元測(cè)試類檢驗(yàn)一下文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-668806.html
package com.example.demo.mapper;
import com.example.demo.model.UserInfo;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest // 當(dāng)前測(cè)試的上下文環(huán)境為 Spring Boot
class UserMapperTest {
// 要測(cè)試誰(shuí) , 就注入誰(shuí)
@Autowired
private UserMapper userMapper;
@Test
void delByIds() {
List<Integer> list = new ArrayList<>();
list.add(6);
list.add(7);
list.add(8);
int result = userMapper.delByIds(list);
System.out.println("刪除了:" + result);
}
}
[外鏈圖片轉(zhuǎn)存中…(img-NhQTewAx-1692756514226)]文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-668806.html
到了這里,關(guān)于Java EE 突擊 13 - MyBatis 查詢數(shù)據(jù)庫(kù)(2)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!