1. Mybatis基礎操作
學習完mybatis入門后,我們繼續(xù)學習mybatis基礎操作。
1.1 需求
需求說明:
-
根據(jù)資料中提供的《tlias智能學習輔助系統(tǒng)》頁面原型及需求,完成員工管理的需求開發(fā)。



通過分析以上的頁面原型和需求,我們確定了功能列表:
-
查詢
-
根據(jù)主鍵ID查詢
-
條件查詢
-
新增
-
更新
-
刪除
-
根據(jù)主鍵ID刪除
-
根據(jù)主鍵ID批量刪除
1.2 準備
實施前的準備工作:
-
準備數(shù)據(jù)庫表
-
創(chuàng)建一個新的springboot工程,選擇引入對應的起步依賴(mybatis、mysql驅動、lombok)
-
application.properties中引入數(shù)據(jù)庫連接信息
-
創(chuàng)建對應的實體類 Emp(實體類屬性采用駝峰命名)
-
準備Mapper接口 EmpMapper
準備數(shù)據(jù)庫表
-- 部門管理
create table dept
(
id int unsigned primary key auto_increment comment '主鍵ID',
name varchar(10) not null unique comment '部門名稱',
create_time datetime not null comment '創(chuàng)建時間',
update_time datetime not null comment '修改時間'
) comment '部門表';
-- 部門表測試數(shù)據(jù)
insert into dept (id, name, create_time, update_time)
values (1, '學工部', now(), now()),
(2, '教研部', now(), now()),
(3, '咨詢部', now(), now()),
(4, '就業(yè)部', now(), now()),
(5, '人事部', now(), now());
-- 員工管理
create table emp
(
id int unsigned primary key auto_increment comment 'ID',
username varchar(20) not null unique comment '用戶名',
password varchar(32) default '123456' comment '密碼',
name varchar(10) not null comment '姓名',
gender tinyint unsigned not null comment '性別, 說明: 1 男, 2 女',
image varchar(300) comment '圖像',
job tinyint unsigned comment '職位, 說明: 1 班主任,2 講師, 3 學工主管, 4 教研主管, 5 咨詢師',
entrydate date comment '入職時間',
dept_id int unsigned comment '部門ID',
create_time datetime not null comment '創(chuàng)建時間',
update_time datetime not null comment '修改時間'
) comment '員工表';
-- 員工表測試數(shù)據(jù)
INSERT INTO emp (id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time)
VALUES
(1, 'jinyong', '123456', '金庸', 1, '1.jpg', 4, '2000-01-01', 2, now(), now()),
(2, 'zhangwuji', '123456', '張無忌', 1, '2.jpg', 2, '2015-01-01', 2, now(), now()),
(3, 'yangxiao', '123456', '楊逍', 1, '3.jpg', 2, '2008-05-01', 2, now(), now()),
(4, 'weiyixiao', '123456', '韋一笑', 1, '4.jpg', 2, '2007-01-01', 2, now(), now()),
(5, 'changyuchun', '123456', '常遇春', 1, '5.jpg', 2, '2012-12-05', 2, now(), now()),
(6, 'xiaozhao', '123456', '小昭', 2, '6.jpg', 3, '2013-09-05', 1, now(), now()),
(7, 'jixiaofu', '123456', '紀曉芙', 2, '7.jpg', 1, '2005-08-01', 1, now(), now()),
(8, 'zhouzhiruo', '123456', '周芷若', 2, '8.jpg', 1, '2014-11-09', 1, now(), now()),
(9, 'dingminjun', '123456', '丁敏君', 2, '9.jpg', 1, '2011-03-11', 1, now(), now()),
(10, 'zhaomin', '123456', '趙敏', 2, '10.jpg', 1, '2013-09-05', 1, now(), now()),
(11, 'luzhangke', '123456', '鹿杖客', 1, '11.jpg', 5, '2007-02-01', 3, now(), now()),
(12, 'hebiweng', '123456', '鶴筆翁', 1, '12.jpg', 5, '2008-08-18', 3, now(), now()),
(13, 'fangdongbai', '123456', '方東白', 1, '13.jpg', 5, '2012-11-01', 3, now(), now()),
(14, 'zhangsanfeng', '123456', '張三豐', 1, '14.jpg', 2, '2002-08-01', 2, now(), now()),
(15, 'yulianzhou', '123456', '俞蓮舟', 1, '15.jpg', 2, '2011-05-01', 2, now(), now()),
(16, 'songyuanqiao', '123456', '宋遠橋', 1, '16.jpg', 2, '2010-01-01', 2, now(), now()),
(17, 'chenyouliang', '123456', '陳友諒', 1, '17.jpg', NULL, '2015-03-21', NULL, now(), now());
創(chuàng)建一個新的springboot工程,選擇引入對應的起步依賴(mybatis、mysql驅動、lombok)

application.properties中引入數(shù)據(jù)庫連接信息
提示:可以把之前項目中已有的配置信息復制過來即可
#驅動類名稱
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#數(shù)據(jù)庫連接的url
spring.datasource.url=jdbc:mysql://localhost:3306/mybatis
#連接數(shù)據(jù)庫的用戶名
spring.datasource.username=root
#連接數(shù)據(jù)庫的密碼
spring.datasource.password=1234
創(chuàng)建對應的實體類Emp(實體類屬性采用駝峰命名)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Emp {
private Integer id;
private String username;
private String password;
private String name;
private Short gender;
private String image;
private Short job;
private LocalDate entrydate; //LocalDate類型對應數(shù)據(jù)表中的date類型
private Integer deptId;
private LocalDateTime createTime;//LocalDateTime類型對應數(shù)據(jù)表中的datetime類型
private LocalDateTime updateTime;
}
準備Mapper接口:EmpMapper
/*@Mapper注解:表示當前接口為mybatis中的Mapper接口
程序運行時會自動創(chuàng)建接口的實現(xiàn)類對象(代理對象),并交給Spring的IOC容器管理
*/
@Mapper
public interface EmpMapper {
}
完成以上操作后,項目工程結構目錄如下:

1.3 刪除
1.3.1 功能實現(xiàn)
頁面原型:

當我們點擊后面的"刪除"按鈕時,前端頁面會給服務端傳遞一個參數(shù),也就是該行數(shù)據(jù)的ID。 我們接收到ID后,根據(jù)ID刪除數(shù)據(jù)即可。
功能:根據(jù)主鍵刪除數(shù)據(jù)
-
SQL語句
-- 刪除id=17的數(shù)據(jù)
delete from emp where id = 17;
Mybatis框架讓程序員更關注于SQL語句
-
接口方法
@Mapper
public interface EmpMapper {
//@Delete("delete from emp where id = 17")
//public void delete();
//以上delete操作的SQL語句中的id值寫成固定的17,就表示只能刪除id=17的用戶數(shù)據(jù)
//SQL語句中的id值不能寫成固定數(shù)值,需要變?yōu)閯討B(tài)的數(shù)值
//解決方案:在delete方法中添加一個參數(shù)(用戶id),將方法中的參數(shù),傳給SQL語句
/**
* 根據(jù)id刪除數(shù)據(jù)
* @param id 用戶id
*/
@Delete("delete from emp where id = #{id}")//使用#{key}方式獲取方法中的參數(shù)值
public void delete(Integer id);
}
@Delete注解:用于編寫delete操作的SQL語句
如果mapper接口方法形參只有一個普通類型的參數(shù),#{…} 里面的屬性名可以隨便寫,如:#{id}、#{value}。但是建議保持名字一致。
-
測試
-
在單元測試類中通過@Autowired注解注入EmpMapper類型對象
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
@Autowired //從Spring的IOC容器中,獲取類型是EmpMapper的對象并注入
private EmpMapper empMapper;
@Test
public void testDel(){
//調用刪除方法
empMapper.delete(16);
}
}
1.3.2 日志輸入
在Mybatis當中我們可以借助日志,查看到sql語句的執(zhí)行、執(zhí)行傳遞的參數(shù)以及執(zhí)行結果。具體操作如下:
-
打開application.properties文件
-
開啟mybatis的日志,并指定輸出到控制臺
#指定mybatis輸出日志的位置, 輸出控制臺
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
開啟日志之后,我們再次運行單元測試,可以看到在控制臺中,輸出了以下的SQL語句信息:

但是我們發(fā)現(xiàn)輸出的SQL語句:delete from emp where id = ?,我們輸入的參數(shù)16并沒有在后面拼接,id的值是使用?進行占位。那這種SQL語句我們稱為 預編譯SQL。
1.3.3 預編譯SQL
1.3.3.1 介紹
預編譯SQL有兩個優(yōu)勢:
-
性能更高
-
更安全(防止SQL注入)

性能更高:預編譯SQL, 編譯一次之后會將編譯后的SQL語句緩存起來,后面再次執(zhí)行這條語句時,不會再次編譯。(只是輸入的參數(shù)不同)
更安全(防止SQL注入): 將敏感字進行轉義,保障SQL的安全性。
1.3.3.2 SQL注入
SQL注入:是通過操作輸入的數(shù)據(jù)來修改事先定義好的SQL語句,以達到執(zhí)行代碼對服務器進行攻擊的方法。
由于沒有對用戶輸入進行充分檢查,而SQL又是拼接而成,在 用戶輸入?yún)?shù)時,在參數(shù)中添加一些SQL關鍵字,達到改變SQL運行結果的目的,也可以完成惡意攻擊。
測試1:使用資料中提供的程序,來驗證SQL注入問題

第1步:進入到DOS

第2步:執(zhí)行以下命令,啟動程序
#啟動存在SQL注入的程序
java -jar sql_Injection_demo-0.0.1-SNAPSHOT.jar

第3步:打開瀏覽器輸入http://localhost:9090/login.html

發(fā)現(xiàn)竟然能夠登錄成功:

以上操作為什么能夠登錄成功呢?
-
由于沒有對用戶輸入內(nèi)容進行充分檢查,而SQL又是字符串拼接方式而成,在用戶輸入?yún)?shù)時,在參數(shù)中添加一些SQL關鍵字,達到改變SQL運行結果的目的,從而完成惡意攻擊。


用戶在頁面提交數(shù)據(jù)的時候人為的添加一些特殊字符,使得sql語句的結構發(fā)生了變化,最終可以在沒有用戶名或者密碼的情況下進行登錄。
測試2:使用資料中提供的程序,來驗證SQL注入問題
第1步:進入到DOS
第2步:執(zhí)行以下命令,啟動程序:
#啟動解決了SQL注入的程序
java -jar sql_prepared_demo-0.0.1-SNAPSHOT.jar
第3步:打開瀏覽器輸入http://localhost:9090/login.html

發(fā)現(xiàn)無法登錄:

以上操作SQL語句的執(zhí)行:

把整個' or '1'='1 作為一個完整的參數(shù),賦值給第2個問號(' or '1'='1進行了轉義,只當做字符串使用)
1.3.3.3 參數(shù)占位符
在Mybatis中提供的參數(shù)占位符有兩種:${...} 、#{...}
-
#{...}
-
執(zhí)行SQL時,會將#{…}替換為?,生成預編譯SQL,會自動設置參數(shù)值
-
使用時機:參數(shù)傳遞,都使用#{…}
-
${...}
-
拼接SQL。直接將參數(shù)拼接在SQL語句中,存在SQL注入問題
-
使用時機:如果對表名、列表進行動態(tài)設置時使用
注意事項:在項目開發(fā)中,建議使用#{...},生成預編譯SQL,防止SQL注入安全。
1.4 新增
功能:新增員工信息

1.4.1 基本新增
員工表結構:

SQL語句:
insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time) values ('songyuanqiao','宋遠橋',1,'1.jpg',2,'2012-10-09',2,'2022-10-01 10:00:00','2022-10-01 10:00:00');
接口方法:
@Mapper
public interface EmpMapper {
@Insert("insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time) values (#{username}, #{name}, #{gender}, #{image}, #{job}, #{entrydate}, #{deptId}, #{createTime}, #{updateTime})")
public void insert(Emp emp);
}
說明:#{...} 里面寫的名稱是對象的屬性名
測試類:
import java.time.LocalDateTime;
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
@Autowired
private EmpMapper empMapper;
@Test
public void testInsert(){
//創(chuàng)建員工對象
Emp emp = new Emp();
emp.setUsername("tom");
emp.setName("湯姆");
emp.setImage("1.jpg");
emp.setGender((short)1);
emp.setJob((short)1);
emp.setEntrydate(LocalDate.of(2000,1,1));
emp.setCreateTime(LocalDateTime.now());
emp.setUpdateTime(LocalDateTime.now());
emp.setDeptId(1);
//調用添加方法
empMapper.insert(emp);
}
}
日志輸出:

1.4.2 主鍵返回
概念:在數(shù)據(jù)添加成功后,需要獲取插入數(shù)據(jù)庫數(shù)據(jù)的主鍵。
如:添加套餐數(shù)據(jù)時,還需要維護套餐菜品關系表數(shù)據(jù)。

業(yè)務場景:在前面講解到的蒼穹外賣菜品與套餐模塊的表結構,菜品與套餐是多對多的關系,一個套餐對應多個菜品。既然是多對多的關系,是不是有一張?zhí)撞筒似分虚g表來維護它們之間的關系。

在添加套餐的時候,我們需要在界面當中來錄入套餐的基本信息,還需要來錄入套餐與菜品的關聯(lián)信息。這些信息錄入完畢之后,我們一點保存,就需要將套餐的信息以及套餐與菜品的關聯(lián)信息都需要保存到數(shù)據(jù)庫當中。
其實具體的過程包括兩步,首先第一步先需要將套餐的基本信息保存了,接下來第二步再來保存套餐與菜品的關聯(lián)信息。套餐與菜品的關聯(lián)信息就是往中間表當中來插入數(shù)據(jù),來維護它們之間的關系。
而中間表當中有兩個外鍵字段,一個是菜品的ID,就是當前菜品的ID,還有一個就是套餐的ID,而這個套餐的 ID 指的就是此次我所添加的套餐的ID,所以我們在第一步保存完套餐的基本信息之后,就需要將套餐的 主鍵值返回來供第二步進行使用。這個時候就需要用到主鍵返回功能。
那要如何實現(xiàn)在插入數(shù)據(jù)之后返回所插入行的主鍵值呢?
-
默認情況下,執(zhí)行插入操作時,是不會主鍵值返回的。如果我們想要拿到主鍵值,需要在Mapper接口中的方法上添加一個Options注解,并在注解中指定屬性useGeneratedKeys=true和keyProperty="實體類屬性名"
主鍵返回代碼實現(xiàn):
@Mapper
public interface EmpMapper {
//會自動將生成的主鍵值,賦值給emp對象的id屬性
@Options(useGeneratedKeys = true,keyProperty = "id")
@Insert("insert into emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time) values (#{username}, #{name}, #{gender}, #{image}, #{job}, #{entrydate}, #{deptId}, #{createTime}, #{updateTime})")
public void insert(Emp emp);
}
測試:
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
@Autowired
private EmpMapper empMapper;
@Test
public void testInsert(){
//創(chuàng)建員工對象
Emp emp = new Emp();
emp.setUsername("jack");
emp.setName("杰克");
emp.setImage("1.jpg");
emp.setGender((short)1);
emp.setJob((short)1);
emp.setEntrydate(LocalDate.of(2000,1,1));
emp.setCreateTime(LocalDateTime.now());
emp.setUpdateTime(LocalDateTime.now());
emp.setDeptId(1);
//調用添加方法
empMapper.insert(emp);
System.out.println(emp.getDeptId());
}
}
1.5 更新
功能:修改員工信息

點擊"編輯"按鈕后,會查詢所在行記錄的員工信息,并把員工信息回顯在修改員工的窗體上(下個知識點學習)
在修改員工的窗體上,可以修改的員工數(shù)據(jù):用戶名、員工姓名、性別、圖像、職位、入職日期、歸屬部門
思考:在修改員工數(shù)據(jù)時,要以什么做為條件呢?
答案:員工id
SQL語句:
update emp set username = 'linghushaoxia', name = '令狐少俠', gender = 1 , image = '1.jpg' , job = 2, entrydate = '2012-01-01', dept_id = 2, update_time = '2022-10-01 12:12:12' where id = 18;
接口方法:
@Mapper
public interface EmpMapper {
/**
* 根據(jù)id修改員工信息
* @param emp
*/
@Update("update emp set username=#{username}, name=#{name}, gender=#{gender}, image=#{image}, job=#{job}, entrydate=#{entrydate}, dept_id=#{deptId}, update_time=#{updateTime} where id=#{id}")
public void update(Emp emp);
}
測試類:
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
@Autowired
private EmpMapper empMapper;
@Test
public void testUpdate(){
//要修改的員工信息
Emp emp = new Emp();
emp.setId(23);
emp.setUsername("songdaxia");
emp.setPassword(null);
emp.setName("老宋");
emp.setImage("2.jpg");
emp.setGender((short)1);
emp.setJob((short)2);
emp.setEntrydate(LocalDate.of(2012,1,1));
emp.setCreateTime(null);
emp.setUpdateTime(LocalDateTime.now());
emp.setDeptId(2);
//調用方法,修改員工數(shù)據(jù)
empMapper.update(emp);
}
}
1.6 查詢
1.6.1 根據(jù)ID查詢
在員工管理的頁面中,當我們進行更新數(shù)據(jù)時,會點擊 “編輯” 按鈕,然后此時會發(fā)送一個請求到服務端,會根據(jù)Id查詢該員工信息,并將員工數(shù)據(jù)回顯在頁面上。

SQL語句:
select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time from emp;
接口方法:
@Mapper
public interface EmpMapper {
@Select("select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time from emp where id=#{id}")
public Emp getById(Integer id);
}
測試類:
@SpringBootTest
class SpringbootMybatisCrudApplicationTests {
@Autowired
private EmpMapper empMapper;
@Test
public void testGetById(){
Emp emp = empMapper.getById(1);
System.out.println(emp);
}
}
執(zhí)行結果:

而在測試的過程中,我們會發(fā)現(xiàn)有幾個字段(deptId、createTime、updateTime)是沒有數(shù)據(jù)值的
1.6.2 數(shù)據(jù)封裝
我們看到查詢返回的結果中大部分字段是有值的,但是deptId,createTime,updateTime這幾個字段是沒有值的,而數(shù)據(jù)庫中是有對應的字段值的,這是為什么呢?

原因如下:
-
實體類屬性名和數(shù)據(jù)庫表查詢返回的字段名一致,mybatis會自動封裝。
-
如果實體類屬性名和數(shù)據(jù)庫表查詢返回的字段名不一致,不能自動封裝。
解決方案:
-
起別名
-
結果映射
-
開啟駝峰命名
起別名:在SQL語句中,對不一樣的列名起別名,別名和實體類屬性名一樣
@Select("select id, username, password, name, gender, image, job, entrydate, " +
"dept_id AS deptId, create_time AS createTime, update_time AS updateTime " +
"from emp " +
"where id=#{id}")
public Emp getById(Integer id);
再次執(zhí)行測試類:

手動結果映射:
通過 @Results及@Result 進行手動結果映射
@Results({@Result(column = "dept_id", property = "deptId"),
@Result(column = "create_time", property = "createTime"),
@Result(column = "update_time", property = "updateTime")})
@Select("select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time from emp where id=#{id}")
public Emp getById(Integer id);
@Results源代碼:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Results {
String id() default "";
Result[] value() default {}; //Result類型的數(shù)組
}
@Result源代碼:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Repeatable(Results.class)
public @interface Result {
boolean id() default false;//表示當前列是否為主鍵(true:是主鍵)
String column() default "";//指定表中字段名
String property() default "";//指定類中屬性名
Class<?> javaType() default void.class;
JdbcType jdbcType() default JdbcType.UNDEFINED;
Class<? extends TypeHandler> typeHandler() default UnknownTypeHandler.class;
One one() default @One;
Many many() default @Many;
}
開啟駝峰命名(推薦):
如果字段名與屬性名符合駝峰命名規(guī)則,mybatis會自動通過駝峰命名規(guī)則映射
駝峰命名規(guī)則: abc_xyz => abcXyz
表中字段名:abc_xyz 類中屬性名:abcXyz
# 在application.properties中添加:
mybatis.configuration.map-underscore-to-camel-case=true
要使用駝峰命名前提是 實體類的屬性 與 數(shù)據(jù)庫表中的字段名嚴格遵守駝峰命名。
1.6.3 條件查詢
在員工管理的列表頁面中,我們需要根據(jù)條件查詢員工信息,查詢條件包括:姓名、性別、入職時間。

通過頁面原型以及需求描述我們要實現(xiàn)的查詢:
-
姓名:要求支持模糊匹配
-
性別:要求精確匹配
-
入職時間:要求進行范圍查詢
-
根據(jù)最后修改時間進行降序排序
SQL語句:
select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time
from emp
where name like '%張%'
and gender = 1
and entrydate between '2010-01-01' and '2020-01-01 '
order by update_time desc;
接口方法:
-
方式一
@Mapper
public interface EmpMapper {
@Select("select * from emp " +
"where name like '%${name}%' " +
"and gender = #{gender} " +
"and entrydate between #{begin} and #{end} " +
"order by update_time desc")
public List<Emp> list(String name, Short gender, LocalDate begin, LocalDate end);
}

以上方式注意事項:
方法中的 形參名和SQL語句中的參數(shù) 占位符名保持一致
模糊查詢使用${...}進行字符串拼接,這種方式呢,由于是字符串拼接,并不是預編譯的形式,所以效率不高、且存在sql注入風險。
-
方式二(解決SQL注入風險)
-
使用MySQL提供的字符串拼接函數(shù):concat('%' , '關鍵字' , '%')
@Mapper
public interface EmpMapper {
@Select("select * from emp " +
"where name like concat('%',#{name},'%') " +
"and gender = #{gender} " +
"and entrydate between #{begin} and #{end} " +
"order by update_time desc")
public List<Emp> list(String name, Short gender, LocalDate begin, LocalDate end);
}
執(zhí)行結果:生成的SQL都 是預編譯的SQL語句(性能高、安全)

1.6.4 參數(shù)名說明
上面我們所編寫的條件查詢功能中,我們需要保證接口中方法的形參名和SQL語句中的參數(shù)占位符名相同
當方法中的形參名和SQL語句中的占位符參數(shù)名不相同時,就會出現(xiàn)以下問題:

參數(shù)名在不同的SpringBoot版本中,處理方案還不同:
-
在springBoot的2.x版本(保證參數(shù)名一致)

springBoot的父工程對compiler編譯插件進行了默認的參數(shù)parameters配置,使得在編譯時,會在生成的字節(jié)碼文 件中保留原方法形參的名稱,所以#{…}里面可以 直接通過形參名獲取對應的值

-
在springBoot的1.x版本/單獨使用mybatis(使用@Param注解來指定SQL語句中的參數(shù)名)

在編譯時,生成的字節(jié)碼文件當中, 不會保留Mapper接口中方法的形參名稱,而是使用var1、var2、...這樣的形參名字,此時要獲取參數(shù)值時,就要 通過@Param注解來指定SQL語句中的參數(shù)名

2. Mybatis的XML配置文件
Mybatis的開發(fā)有兩種方式:
-
注解
-
XML
2.1 XML配置文件規(guī)范
使用Mybatis的注解方式,主要是來完成一些簡單的增刪改查功能。如果需要實現(xiàn)復雜的SQL功能,建議使用XML來配置映射語句,也就是將SQL語句寫在XML配置文件中。
在Mybatis中使用XML映射文件方式開發(fā),需要符合一定的規(guī)范:
-
XML映射文件的名稱與Mapper接口名稱一致,并且將XML映射文件和Mapper接口放置在相同包下(同包同名)
-
XML映射文件的namespace屬性為Mapper接口全限定名一致
-
XML映射文件中sql語句的id與Mapper接口中的方法名一致,并保持返回類型一致。

<select>標簽:就是用于 編寫select查詢語句的。
resultType屬性,指的是 查詢返回的單條記錄所封裝的類型。
2.2 XML配置文件實現(xiàn)
第1步:創(chuàng)建XML映射文件



第2步:編寫XML映射文件
xml映射文件中的dtd約束,直接從mybatis官網(wǎng)復制即可
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="">
</mapper>
配置:XML映射文件的namespace屬性為Mapper接口全限定名

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.EmpMapper">
</mapper>
配置:XML映射文件中sql語句的id與Mapper接口中的方法名一致,并保持返回類型一致

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.EmpMapper">
<!--查詢操作-->
<select id="list" resultType="com.itheima.pojo.Emp">
select * from emp
where name like concat('%',#{name},'%')
and gender = #{gender}
and entrydate between #{begin} and #{end}
order by update_time desc
</select>
</mapper>
運行測試類,執(zhí)行結果:

2.3 MybatisX的使用
MybatisX是一款基于IDEA的快速開發(fā)Mybatis的插件,為效率而生。
MybatisX的安裝:

可以通過MybatisX快速定位:

MybatisX的使用在后續(xù)學習中會繼續(xù)分享
學習了Mybatis中XML配置文件的開發(fā)方式了,大家可能會存在一個疑問:到底是使用注解方式開發(fā)還是使用XML方式開發(fā)?
官方說明: 入門_MyBatis中文網(wǎng)

結論:使用Mybatis的注解,主要是來完成一些簡單的增刪改查功能。如果需要實現(xiàn)復雜的SQL功能,建議使用XML來配置映射語句。
3. Mybatis動態(tài)SQL
3.1 什么是動態(tài)SQL
在頁面原型中,列表上方的條件是動態(tài)的,是可以不傳遞的,也可以只傳遞其中的1個或者2個或者全部。


而在我們剛才編寫的SQL語句中,我們會看到,我們將三個條件直接寫死了。 如果頁面只傳遞了參數(shù)姓名name 字段,其他兩個字段 性別 和 入職時間沒有傳遞,那么這兩個參數(shù)的值就是null。
此時,執(zhí)行的SQL語句為:

這個查詢結果是不正確的。正確的做法應該是:傳遞了參數(shù),再組裝這個查詢條件;如果沒有傳遞參數(shù),就不應該組裝這個查詢條件。
比如:如果姓名輸入了"張", 對應的SQL為:
select * from emp where name like '%張%' order by update_time desc;
如果姓名輸入了"張",,性別選擇了"男",則對應的SQL為:
select * from emp where name like '%張%' and gender = 1 order by update_time desc;
SQL語句會隨著用戶的輸入或外部條件的變化而變化,我們稱為:動態(tài)SQL。

在Mybatis中提供了很多實現(xiàn)動態(tài)SQL的標簽,我們學習Mybatis中的動態(tài)SQL就是掌握這些動態(tài)SQL標簽。
3.2 動態(tài)SQL-if
<if>:用于判斷條件是否成立。
使用test屬性進行條件判斷,如果條件為true,則拼接SQL。
<if test="條件表達式">
要拼接的sql語句
</if>
接下來,我們就通過<if>標簽來改造之前條件查詢的案例。
3.2.1 條件查詢
示例:把SQL語句改造為動態(tài)SQL方式
-
原有的SQL語句
<select id="list" resultType="com.itheima.pojo.Emp">
select * from emp
where name like concat('%',#{name},'%')
and gender = #{gender}
and entrydate between #{begin} and #{end}
order by update_time desc
</select>
-
動態(tài)SQL語句
<select id="list" resultType="com.itheima.pojo.Emp">
select * from emp
where
<if test="name != null">
name like concat('%',#{name},'%')
</if>
<if test="gender != null">
and gender = #{gender}
</if>
<if test="begin != null and end != null">
and entrydate between #{begin} and #{end}
</if>
order by update_time desc
</select>
測試方法:
@Test
public void testList(){
//性別數(shù)據(jù)為null、開始時間和結束時間也為null
List<Emp> list = empMapper.list("張", null, null, null);
for(Emp emp : list){
System.out.println(emp);
}
}
執(zhí)行的SQL語句:

下面呢,我們修改測試方法中的代碼,再次進行測試,觀察執(zhí)行情況:
@Test
public void testList(){
//姓名為null
List<Emp> list = empMapper.list(null, (short)1, null, null);
for(Emp emp : list){
System.out.println(emp);
}
}
執(zhí)行結果:

因為沒傳name,所以and多一個

再次修改測試方法中的代碼,再次進行測試:
@Test
public void testList(){
//傳遞的數(shù)據(jù)全部為null
List<Emp> list = empMapper.list(null, null, null, null);
for(Emp emp : list){
System.out.println(emp);
}
}
執(zhí)行的SQL語句:

以上問題的解決方案:使用<where>標簽代替SQL語句中的where關鍵字
-
<where>只會在子元素有內(nèi)容的情況下才插入where子句,而且會自動去除子句的開頭的AND或OR
<select id="list" resultType="com.itheima.pojo.Emp">
select * from emp
<where>
<!-- if做為where標簽的子元素 -->
<if test="name != null">
and name like concat('%',#{name},'%')
</if>
<if test="gender != null">
and gender = #{gender}
</if>
<if test="begin != null and end != null">
and entrydate between #{begin} and #{end}
</if>
</where>
order by update_time desc
</select>
測試方法:
@Test
public void testList(){
//只有性別
List<Emp> list = empMapper.list(null, (short)1, null, null);
for(Emp emp : list){
System.out.println(emp);
}
}
執(zhí)行的SQL語句:

3.2.2 更新員工
案例:完善更新員工功能,修改為動態(tài)更新員工數(shù)據(jù)信息
-
動態(tài)更新員工信息,如果更新時傳遞有值,則更新;如果更新時沒有傳遞值,則不更新
-
解決方案:動態(tài)SQL
修改Mapper接口:
@Mapper
public interface EmpMapper {
//刪除@Update注解編寫的SQL語句
//update操作的SQL語句編寫在Mapper映射文件中
public void update(Emp emp);
}
修改Mapper映射文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.EmpMapper">
<!--更新操作-->
<update id="update">
update emp
set
<if test="username != null">
username=#{username},
</if>
<if test="name != null">
name=#{name},
</if>
<if test="gender != null">
gender=#{gender},
</if>
<if test="image != null">
image=#{image},
</if>
<if test="job != null">
job=#{job},
</if>
<if test="entrydate != null">
entrydate=#{entrydate},
</if>
<if test="deptId != null">
dept_id=#{deptId},
</if>
<if test="updateTime != null">
update_time=#{updateTime}
</if>
where id=#{id}
</update>
</mapper>
測試方法:
@Test
public void testUpdate2(){
//要修改的員工信息
Emp emp = new Emp();
emp.setId(20);
emp.setUsername("Tom111");
emp.setName("湯姆111");
emp.setUpdateTime(LocalDateTime.now());
//調用方法,修改員工數(shù)據(jù)
empMapper.update(emp);
}
執(zhí)行的SQL語句:

再次修改測試方法,觀察SQL語句執(zhí)行情況:
@Test
public void testUpdate2(){
//要修改的員工信息
Emp emp = new Emp();
emp.setId(20);
emp.setUsername("Tom222");
//調用方法,修改員工數(shù)據(jù)
empMapper.update(emp);
}
執(zhí)行的SQL語句:

以上問題的解決方案:使用<set>標簽代替SQL語句中的set關鍵字
-
<set>:動態(tài)的在SQL語句中插入set關鍵字,并會刪掉多余的逗號。
(用于update語句中)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.EmpMapper">
<!--更新操作-->
<update id="update">
update emp
<!-- 使用set標簽,代替update語句中的set關鍵字 -->
<set>
<if test="username != null">
username=#{username},
</if>
<if test="name != null">
name=#{name},
</if>
<if test="gender != null">
gender=#{gender},
</if>
<if test="image != null">
image=#{image},
</if>
<if test="job != null">
job=#{job},
</if>
<if test="entrydate != null">
entrydate=#{entrydate},
</if>
<if test="deptId != null">
dept_id=#{deptId},
</if>
<if test="updateTime != null">
update_time=#{updateTime}
</if>
</set>
where id=#{id}
</update>
</mapper>
再次執(zhí)行測試方法,執(zhí)行的SQL語句:

小結
-
<if>
-
用于判斷條件是否成立,如果條件為true,則拼接SQL
-
形式:
<if test="name != null"> … </if>
-
<where>
-
where元素只會在子元素有內(nèi)容的情況下才插入where子句,而且會自動去除子句的開頭的AND或OR
-
<set>
-
動態(tài)地在行首插入 SET 關鍵字,并會刪掉額外的逗號。(用在update語句中)
3.3 動態(tài)SQL-foreach
案例:員工刪除功能(既支持刪除單條記錄,又支持批量刪除)

SQL語句:
delete from emp where id in (1,2,3);
Mapper接口:
@Mapper
public interface EmpMapper {
//批量刪除
public void deleteByIds(List<Integer> ids);
}
XML映射文件:
-
使用<foreach>遍歷deleteByIds方法中傳遞的參數(shù)ids集合
<foreach collection="要遍歷的集合名稱" item="集合遍歷出來的元素/項" separator="每一次遍歷使用的分隔符"
open="遍歷開始前拼接的片段" close="遍歷結束后拼接的片段">
</foreach>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.mapper.EmpMapper">
<!--刪除操作-->
<delete id="deleteByIds">
delete from emp where id in
<foreach collection="ids" item="id" separator="," open="(" close=")">
#{id}
</foreach>
</delete>
</mapper>

執(zhí)行的SQL語句:

3.4 動態(tài)SQL-sql&include
問題分析:
-
在xml映射文件中配置的SQL,有時可能會存在很多重復的片段,此時就會存在很多冗余的代碼


我們可以對重復的代碼片段進行抽取,將其通過<sql>標簽封裝到一個SQL片段,然后再通過<include>標簽進行引用。
-
<sql>:定義可重用的SQL片段
-
<include>:通過屬性refid,指定包含的SQL片段

SQL片段: 抽取重復的代碼文章來源:http://www.zghlxwxcb.cn/news/detail-564429.html
<sql id="commonSelect">
select id, username, password, name, gender, image, job, entrydate, dept_id, create_time, update_time from emp
</sql>
然后通過<include> 標簽在原來抽取的地方進行引用。操作如下:文章來源地址http://www.zghlxwxcb.cn/news/detail-564429.html
<select id="list" resultType="com.itheima.pojo.Emp">
<include refid="commonSelect"/>
<where>
<if test="name != null">
name like concat('%',#{name},'%')
</if>
<if test="gender != null">
and gender = #{gender}
</if>
<if test="begin != null and end != null">
and entrydate between #{begin} and #{end}
</if>
</where>
order by update_time desc
</select>
到了這里,關于Mybatis從0到1 SQL注入 參數(shù)占位符 XML配置 動態(tài)SQL的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!