?前言??
??????SSM專欄更新中,各位大佬覺得寫得不錯,支持一下,感謝了!??????
Spring + Spring MVC + MyBatis_冷兮雪的博客-CSDN博客
前面我們講解了MyBatis增刪改查基本操作,下面我們來深入了解MyBatis其中不同和需要注意的地方。
一、查詢操作??
1、單表查詢??
下面我們來實現(xiàn)?下根據(jù)用戶 id 查詢用戶信息的功能
UserController?實現(xiàn)代碼如下:
//url 路徑名直接全部小寫即可
@RequestMapping("/getuserbyid")
public Userinfo geUserById(Integer id){
if (id==null)
return null;
return userService.getUserById(id);
}
?UserMapper?實現(xiàn)代碼如下:
/**
* 根據(jù)用戶id查詢用戶信息
* @param id
* @return
*/
Userinfo getUserById(@Param("id") Integer id);
UserMapper.xml?實現(xiàn)代碼如下:
<select id="getUserById" resultType="com.example.ssmdemo1.entity.Userinfo">
select * from userinfo where id=${id}
</select>
Ⅰ、參數(shù)占位符 #{} 和 ${}??
-
#{}:預(yù)編譯處理。
-
${}:字符直接替換。
預(yù)編譯處理是指:MyBatis 在處理#{}時,會將 SQL 中的 #{} 替換為?號,使用?PreparedStatement 的 set 方法來賦值。直接替換:是MyBatis 在預(yù)處理 ${} 時,就會把 ${} 替換成變量的值。
上面代碼我們使用的是${},去傳遞Integer(整數(shù))類型的參數(shù)時,是沒有問題的,但如果傳遞的是String類型的話,程序就會報錯。
下面我們通過 根據(jù)用戶名查詢用戶(getUserByName)來看看
?這就直接報錯了,說是沒有admin這個用戶,這是因為${}是直接替換值(不會管你是什么類型,都直接替換),而SQL語句中字符串需要使用單引號,這就會查詢不到,報錯。
正確SQL:?
兩者區(qū)別總結(jié):
1、
#{}
:安全參數(shù)占位符
#{}
是MyBatis的預(yù)編譯語句中的參數(shù)占位符,用于傳遞參數(shù)值。它會自動進(jìn)行參數(shù)值的類型轉(zhuǎn)換和防止SQL注入攻擊。- 在使用
#{}
時,MyBatis會將參數(shù)值通過JDBC的PreparedStatement接口進(jìn)行預(yù)編譯,參數(shù)值會被當(dāng)做字符串類型處理,然后由JDBC驅(qū)動來負(fù)責(zé)將其轉(zhuǎn)換成對應(yīng)的數(shù)據(jù)庫類型,這樣可以避免SQL注入問題。 - 例子:
SELECT * FROM users WHERE id = #{userId}
2、
${}
:字符串替換占位符
${}
是字符串替換占位符,用于直接將參數(shù)的值替換到SQL語句中。在使用${}
時,參數(shù)值會被直接替換進(jìn)SQL語句中,不會進(jìn)行預(yù)編譯或類型轉(zhuǎn)換。- 由于
${}
直接替換參數(shù)值到SQL語句中,可能存在SQL注入的風(fēng)險,因此不建議在動態(tài)SQL中使用${}
來傳遞用戶輸入的參數(shù)。 - 例子:
SELECT * FROM users WHERE id = ${userId}
那這為什么還有${}去傳遞參數(shù)呢?全部使用#{}不是更好?
Ⅱ、${}優(yōu)點??
在進(jìn)行排序時(需要傳遞關(guān)鍵字時)需要使用到${},而?#{sort} 就不能實現(xiàn)排序查詢了,因為使用?#{sort} 查詢時, 如果傳遞的值為 String 則會加單引號,就會導(dǎo)致 sql 錯誤。
UserMapper接口:
//根據(jù)id查詢用戶 并且進(jìn)行排序
List<Userinfo> getAllByOrder(@Param("order") String order);
UserMapper.xml:
<select id="getAllByOrder" resultType="com.example.ssmdemo1.entity.Userinfo">
select * from userinfo order by id ${order}
</select>
@Test
void getAllByOrder() {
List<Userinfo> list = userMapper.getAllByOrder("asc");
System.out.println(list);
}
單元測試成功:
Ⅲ、SQL 注入問題???
UserMapper接口:
Userinfo login(@Param("username")String username,@Param("password")String password);
UserMapper.xml:
<select id="login" resultType="com.example.ssmdemo1.entity.Userinfo">
select *from userinfo where usernaem='${username}' and password='${password}'
</select>
因為${}是直接引用,所以我們加上了單引號。?這樣就和使用#{}是一樣的了
單元測試:
@Test
void login() {
String username="2";
String password="2";
Userinfo userinfo=userMapper.login(username,password);
System.out.println("用戶登錄"+(userinfo==null?"失敗":"成功"));
}
?可以看到此時用戶是登錄成功的:?
但是這樣寫有SQL注入的風(fēng)險,我們修改代碼如下,然后運行代碼
@Test
void login() {
String username="2";
String password="'or 1 ='1";
Userinfo userinfo=userMapper.login(username,password);
System.out.println("用戶登錄"+(userinfo==null?"失敗":"成功"));
}
單元測試:
可以看到上面單元測試失敗了,但仔細(xì)看,是因為返回了5個Userinfo對象,但我只需要接收一個
所以報錯了,如果接受的是List<Userinfo>就不會報錯了
UserMapper接口:
List<Userinfo> login(@Param("username")String username, @Param("password")String password);
單元測試:
@Test
void login() {
String username="2";
String password="' or 1='1";
List<Userinfo> userinfo=userMapper.login(username,password);
System.out.println("用戶登錄"+(userinfo==null?"失敗":"成功"));
}
單元測試成功:
可以看到這是非??膳碌?,居然把我所有用戶信息返回了(數(shù)據(jù)庫中一共有五個用戶),也就是說,你想使用哪個用戶登錄就可以使用哪個用戶登錄。
如果使用#{},可能存在這個問題嗎?
<select id="login" resultType="com.example.ssmdemo1.entity.Userinfo">
select *from userinfo where username=#{username} and password=#{password}
</select>
?單元測試失敗:
?結(jié)論:用于查詢的字段,盡量使用?#{} 預(yù)查詢的方式,而需要傳遞關(guān)鍵字時,使用${}
Ⅳ、like查詢??
在使用like查詢時,使用#{}會報錯,下面我們來看看是怎么回事。
UserMapper接口:
List<Userinfo> getListByName(@Param("username")String username);
UserMapper.xml:
<select id="getListByName" resultType="com.example.ssmdemo1.entity.Userinfo">
select * from userinfo where username like '%#{username}%'
</select>
單元測試:
@Test
void getListByName() {
String username="n";
List<Userinfo> list=userMapper.getListByName(username);
System.out.println("list:"+list);
}
?運行報錯:
這是因為使用#{}會當(dāng)作字符串進(jìn)行替換,就變成下面這樣了
select * from userinfo where username like '%'n'%'
我們替換${}試試:
但是前面說了使用${}有SQL注入的風(fēng)險,所有這是不能直接使用?${},可以考慮使用?mysql 的內(nèi)置函數(shù) concat() 來處理,實現(xiàn)代碼如下:?
<select id="findUserByName3" resultType="com.example.demo.model.User">
select * from userinfo where username like concat('%',#{username},'%')
</select>
單元測試成功:?
?在使用like查詢時應(yīng)該搭配concat()函數(shù)使用。
2、多表查詢??
如果是增、刪、改返回搜影響的行數(shù),那么在 UserMapper.xml 中是可以不設(shè)置返回的類型的,如:
<insert id="add">
insert into userinfo(username,password) values(#{username},#{password})
</insert>
<delete id="delById">
delete from userinfo where id=#{id}
</delete>
<update id="upUserName">
update userinfo set username=#{username} where id=#{id}
</update>
然而即使是最簡單查詢用戶的名稱也要設(shè)置返回的類型,否則會出現(xiàn)如下錯誤:
查詢不設(shè)置返回類型的錯誤示例演示:
controller 代碼:
@RequestMapping("/getname")
public String getNameById(Integer id) {
return userService.getNameById(id);
}
<select id="getNameById">
select username from userinfo where id=#{id}
</select>
訪問接口執(zhí)行結(jié)果如下:
顯示運行了?個查詢但沒有找到結(jié)果映射,也就是說對于 <select> 查詢標(biāo)簽來說至少需要兩個屬性:
-
id 屬性:用于標(biāo)識實現(xiàn)接口中的那個方法;
-
結(jié)果映射屬性:結(jié)果映射有兩種實現(xiàn)標(biāo)簽:<resultMap> 和 <resultType>。
Ⅰ、返回類型:resultType ???
絕大數(shù)查詢場景可以使? resultType 進(jìn)行返回,如下代碼所示:
<select id="getNameById" resultType="java.lang.String">
select username from userinfo where id=#{id}
</select>
<select id="getUserByName" resultType="com.example.ssmdemo1.entity.Userinfo">
select * from userinfo where username=#{username}
</select>
它的優(yōu)點是使用方便,直接定義到某個實體類即可。
Ⅱ、返回字典映射:resultMap???
-
字段名稱和程序中的屬性名不同的情況,可使用?resultMap 配置映射;
-
?對?和?對多關(guān)系可以使用?resultMap 映射并查詢數(shù)據(jù)。
當(dāng)程序中的屬性值與數(shù)據(jù)庫中的字段名不一樣時??
@Data
public class Userinfo {
private Integer id;
private String name;//數(shù)據(jù)庫名為:username
private String password;
private String photo;
private LocalDateTime createTime;
private LocalDateTime updateTime;
private Integer state;
}
?我們?nèi)卧獪y試,進(jìn)行用戶的查詢:
@Test
void getUserByName() {
Userinfo userinfo=userMapper.getUserByName("admin");
System.out.println(userinfo);
}
?打印出來就發(fā)現(xiàn),name沒有被賦值,為null,這就是因為字段名與屬性值不一樣的結(jié)果。
這個時候就可以使用?resultMap 了,resultMap 的使用如下:
?UserMapper.xml:
<resultMap id="baseMap" type="com.example.demo.entity.Userinfo">
<id column="id" property="id"></id>
<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="getUserByName" resultMap="baseMap">
select * from userinfo where username=#{username}
</select>
這樣查詢的結(jié)果就有值了,如下圖所示:
或者使用as關(guān)鍵字(數(shù)據(jù)庫重命名)??
如果你一定需要使用resultType,也是可以實現(xiàn)的:
<select id="getUserByName" resultType="com.example.ssmdemo1.entity.Userinfo">
select id,username as name,password,photo,createtime,updatetime from userinfo where username=#{username}
</select>
Ⅲ、多表查詢??
在多表查詢時,如果使用?resultType 標(biāo)簽,在?個類中包含了另?個對象是查詢不出來被包含的對象的,比如以下實體類:
@Data
public class ArticleInfo {
private Integer id;
private String title;
private String content;
private LocalDateTime createtime;
private LocalDateTime updatetime;
private Integer rcount;
// 包含了 userinfo 對象
private UserInfo user;
}
程序的執(zhí)行結(jié)果如下圖所示:
此時我們就需要使用特殊的手段來實現(xiàn)聯(lián)表查詢了。
通過VO對象??
ArticleInfo
package com.example.ssmdemo1.entity;
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class ArticleInfo {
private Integer id;
private String title;
private String content;
private LocalDateTime createtime;
private LocalDateTime updatetime;
private int uid;
private Integer rcount;
private Integer state;
}
VO對象
package com.example.ssmdemo1.entity.vo;
import com.example.ssmdemo1.entity.ArticleInfo;
public class ArticleInfoVO extends ArticleInfo {
private String username;
@Override
public String toString() {
return "ArticleinfoVO{" +
"username='" + username + '\'' +
"} " + super.toString();
}
}
?ArticleMapper.xml
進(jì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.ssmdemo1.mapper.ArticleMapper">
<select id="getById" resultType="com.example.ssmdemo1.entity.vo.ArticleInfoVO">
select a.*,u.username from articleinfo a
left join userinfo u on u.id=a.uid
where a.id=#{id}
</select>
</mapper>
單元測試:
package com.example.ssmdemo1.mapper;
import com.example.ssmdemo1.entity.vo.ArticleInfoVO;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
class ArticleMapperTest {
@Autowired
private ArticleMapper articleMapper;
@Test
void getById() {
ArticleInfoVO articleInfoVO=articleMapper.getById(5);
System.out.println(articleInfoVO);
}
}
單元測試成功,查詢到了用戶id為5的 文章表的文章信息 和 用戶表中的用戶名:
大部分時候多表聯(lián)查解決方案:
?聯(lián)表查詢語句(left join/inner)+xxxVO(新建的實體類)就可以解決文章來源:http://www.zghlxwxcb.cn/news/detail-627988.html
文章來源地址http://www.zghlxwxcb.cn/news/detail-627988.html
到了這里,關(guān)于MyBatis查詢數(shù)據(jù)庫(3)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!