先講一下準備工作整體流程要做什么
我們要基于一個員工管理系統作為案例,進行員工信息的【增、刪、改、查】
原理就是用Mybatis通過java語言來執行sql語句,來達到【增、刪、改、查】
一、準備工作
1、引入數據庫數據
首先我們把一個員工、部門表的數據在數據庫里建好先
老弟們我直接把黑馬的資源代碼放這里了,各位不用再去找、網盤下載,直接拿我下面這個代碼放數據庫查詢控制臺執行一下就行
-- 數據準備:-- 部門管理
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 '創建時間',update_time datetime not null comment '修改時間'
) comment '部門表';
insert into dept (id, name, create_time, update_time) values(1,'學工部',now(),now()),(2,'教研部',now(),now()),(3,'咨詢部',now(),now()), (4,'就業部',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 教研主管',entrydate date comment '入職時間',dept_id int unsigned comment '部門id, 說明: 1 學工部,2 教研部, 3 咨詢部, 4 就業部, 5 人事部',create_time datetime not null comment '創建時間',update_time datetime not null comment '修改時間'
) comment '員工表';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,'2007-01-01',2,now(),now()),(17,'chenyouliang','123456','陳友諒',1,'17.jpg',NULL,'2015-03-21',NULL,now(),now());
2、然后就是之前那些創建springboot工程、引入mybatis等等依賴的配置操作
因為我之前第一篇講過了就不再演示了,跟著我配置好了的可以不用再創建多余的工程,直接在原先的數據庫多加兩個表而已,反正還是用原來的spring boot工程項目來連接同一個數據庫
配置那一篇的鏈接:后端之路第三站(Mybatis)——入門配置-CSDN博客
當然如果你非要另開一個數據庫來連接的話,那你就重新配置一遍并連接這個數據庫吧,不過注意,可以在引入依賴的時候,除了【Mybatis Framework】和【MySQL Driver】還可以直接再勾選【Developer?Tools】的【Lombok】依賴,因為這樣就不用我們再去【pom.xml文件】那里在手動寫代碼的形式引入lombok的依賴了
3、然后連接你的數據庫
這里我懶得換數據庫,還是在原來的數據庫建兩個表而已,所以我就不演示了,想看的還是看我這一篇:后端之路第三站(Mybatis)——入門配置-CSDN博客
4、然后對應你建的表,在java這創建對應的實體類
別忘了
整型數:int ? 在java對應 ? Integer
字符串:varchar( ) ? 在java對應 ? String
? ? ? ? ? ? ? ?char( ) ? 在java對應 ? String
短整型數:tinyint ? 在java對應 ? Short
日期:date?在java對應 ? LocalDate
時間:datetime? 在java對應 ? LocalDateTime
員工表的實體類
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.time.LocalDate;
import java.time.LocalDateTime;@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;private Integer dept_id;private LocalDateTime create_time;private LocalDateTime update_time;
}
部門表的實體類
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.time.LocalDateTime;@Data
@NoArgsConstructor
@AllArgsConstructor
public class Dept {private Integer id;private String name;private LocalDateTime create_time;private LocalDateTime update_time;
}
這里我建議各位自己不要直接拿我代碼復制粘貼,最好自己收敲一遍,加深代碼流程記憶
6、到mapper目錄創建【對應xxx表的SQL語句執行】的接口
二、開始sql語句操作
噢忘了跟各位提一嘴,idea的右邊側邊欄是可以連接數據庫的
點開這里,點擊“+”加號添加數據源,就跟在數據庫軟件里連接數據庫一樣的,寫個用戶名密碼啥的,我已經鏈接了我就不展示了,有手就行
然后每當我們在數據庫創建好新的表之后,都要記得去idea這里刷新一下,不然的話idea不知道你多了兩表,寫代碼的時候就沒有相關的提示了
1、刪除
刪除的sql語句是:【 delete from 表 where 條件 】
刪除的Mybatis注解是:【 @Delete( "delete from 表 where 條件"? )? 】
那么在MySQL里我們是這么寫
在Mybatis就是
哪有的人就會問:你這where id = 17不就寫死了嗎?那如果我想動態的刪除員工,而不是固定死只刪除id為17的員工,怎么辦?還有你下面那個public void delete();又是什么玩意?
比如我要實現管理系統里動態刪除,點那個就刪哪個
@XXX是注解,對于執行sql語句的注解,你要是想執行的話不能單單靠一個注解就執行啊,你必須得帶一個方法,讓外部調用你這個方法,才能執行你這個@注解來操作數據庫啊?
那么好,這里我們就應該在下面寫一個方法,首先考慮我們這個sql語句不是查詢語句,不需要返回!!!所以直接類型為void無返回
然后加參數!在外面調用這個接口的這個方法時會動態地傳入參數,這里我們考慮根據id來刪,那么比如在管理系統里,我點擊第2個人的“刪除”,就傳入一個【id=2】的參數
現在我們有了參數還剩最后一步,改@Delete這個注解里的sql代碼了,很簡單【#{ 變量 }】這樣就可以動態綁定變量了!!是不是很像前端的【`${ 變量 }`】?
完整代碼
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.beans.factory.annotation.Autowired;@Mapper
public interface EmpMapper {//@Sql的注解( "要執行的sql語句" )@Delete("delete from emp where id = #{id}")//對應這個sql語句執行的一些相關方法public void delete(Integer id);
}
ok,然后現在測試就行了,老步驟:
1、test的...ApplicationTests.java文件
2、在類里面@Autowired注解,然后創建EmpMapper的對象變量使用
3、然后@Test注解直接跟上要運行的方法,在方法里調用EmpMapper的delete方法并傳入實參
運行搞定
總結
#{ 變量 } 和 ${ 變量 }:
在application.properties文件配置好這個下面這個代碼,以后就可以在控制臺看到具體的日志(也就是直觀的mybatis執行sql語句的過程)
不用記,直接復制
#配置mybatis的日志,指定輸出到控制臺
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
那么為什么我們where id = #{ 變量 } 在控制臺日志里會變成?where id = ?
原理是:
#{ 變量 } 的語法會生成【預編譯語句】
而${ 變量?} 的語法不會生成【預編譯語句】,而是像前端的${ 變量?} 一樣直接把變量直接拼接到字符串
至于有什么用?預編譯的 #{ } 和 ${ } 有什么優劣?
簡單的例子,不用深究了解
如果用?${ }?直接字符串拼接的形式,就會
根據一整個語句,字符串里的【or、and、=】這些字符會被當成【判斷邏輯符號】用,如果條件永遠為真,那就算這個密碼錯的也可以登錄
然而?#{ }?的是預編譯,字符串以一整個參數形式傳入,【or、and、=】這些字符還是當成普通字符串來判斷
2、新增
新增的sql語句是:
(表里所有字段都傳值時) —>【 insert into 表?values (對應的所有字段值1),(對應的所有字段值2)......?】
(表里指定部分字段傳值時) —>【 insert into 表(字段1,字段2...) values (對應的字段的值1),(對應的字段的值2)......?】
新增的Mybatis注解是:
(表里所有字段都傳值時) —>【 @Insert( "insert into 表?values (對應的所有字段值1),(對應的所有字段值2)......?" )? ?】
(表里指定部分字段傳值時) —>【 @Insert( "insert into 表(字段1,字段2...) values (對應的字段的值1),(對應的字段的值2)......"?)? 】
現在我們想要完成一個新增員工的這么一個功能
在數據庫里sql語句是
(不會的先去學MySQL的知識,這里就是根據“用戶名、姓名、性別、圖像、職位、入職日期、歸屬部門、這個信息創建時間、這個信息的更新時間”來執行sql的新增信息,【這個信息創建時間、這個信息的更新時間】這兩是一般更新新增信息固定帶有的信息)
那么在Mybatis就這么寫
還是那個問題,這是死數據,我需要動態傳值,那就把value里寫成 #{ } 參數
然后public void insert( )里定義參數,好讓外部調用時傳入實參
注意:
【@注解里的sql語句】的參數名、表名要跟數據庫的一樣;因為它接收對應數據庫的值
【@注解里的values】的的參數是要跟java里的實體類的參數名一樣!!!因為它接收java的值
???????
【方法里】的參數是要跟java里的實體類的參數名、類名一樣!!!因為它接收java的值
然后因為新增操作涉及大量參數,所以在java里傳多個值可以直接用【對象】!!所以【方法里】直接傳【對象】即可,然后【@注解里的values】的 #{ 變量 } 會自動解析出對象里的對應的變量的屬性
那么就到最后一步,在外部ApplicationTest方法里調用方法
但是下面注意:這樣就錯了!!我們在接口定義的參數是一整個對象,不是這么零散的參數
那就要先構建一個實體類的對象,把這些參數作為對象的成員變量裝進去,然后整個對象給回Mapper接口
@Test
public void testInsert(){//這樣就錯了!!我們在接口定義的參數是一整個對象,不是這么零散的參數//empMapper.insert("CZM","岑梓銘",(short)1,"1.jpg",(short)3, LocalDate.of(2024,1,1),1, LocalDateTime.now(),LocalDateTime.now());//得先構建一個對象,然后把對應的值調用setter方法設置//對象里沒用的成員變量就不用設置,不設置的變量就是默認值,也不會報錯也不會有影響Emp emp1 = new Emp();emp1.setJob((short)3);emp1.setGender((short)1);emp1.setUsername("CZM");emp1.setName("岑梓銘");emp1.setImage("1.jpg");emp1.setEntrydate(LocalDate.of(2024,1,1));emp1.setDept_id(1);emp1.setCreate_time(LocalDateTime.now());emp1.setUpdate_time(LocalDateTime.now());empMapper.insert(emp1);
}
能看到又報錯了,很明顯我們#{ }的地方有寫錯java里對應的變量
改一下就成功了
3、主鍵返回
上一個新增操作,我們因為在數據庫設置了id字段是自增的主鍵,那么即使我們不給id傳值,它也會自動在數據庫增加id的值,這時數據庫的基礎知識
那么既然有值,我們能不能在java拿到數據庫返回的id這個主鍵的值呢?不行
因為默認普通的插入是不會有返回值的,那要有返回值咋辦?
加一個注解
@Option(useGeneratedKeys = true, keyProperty = "主鍵那個字段")
現在再執行一下
成功
4、更新
更新的sql語句是:
【 update set? 表? 字段1=新值1,字段2=值2......? where? 條件?】
新增的Mybatis注解是:
【 @Update( "update set? 表? 字段1=新值1,字段2=值2......? where? 條件?" )? ?】
現在要實現這么個功能,點擊編輯可以更改員工信息
那么根據圖片的需求信息總結,需要更新的字段值是:username 、name、gender、image、job 、entrydate 、dept_id、還有一個【update_time】(新增的時候要有“新建時間” 和 “更新時間”,那更新就要有 “更新時間”)
sql里就應該這么寫
那么現在Mybatis就應該這么寫:(我就直接寫成動態傳參的了,不示范死數據格式了)
成功更新
5、查詢
查詢的sql語句是:
【 select? *? from? 表??where? 條件?】
查詢的Mybatis注解是:
【 @Select( "select? *? from? 表??where? 條件" )? ?】
sql語句是這樣
Mybatis就應該這么寫
成功
查詢中的一些數據封裝問題:
用我這篇文章里的代碼的朋友們在剛剛的查詢操作中應該不會有什么問題,因為我的代碼里的【Java的實體類Emp里的成員變量的名】和【數據庫的emp表的字段名】是一樣的,所以查詢時會對應映射、封裝數據
但是有些跟著黑馬程序員視頻的朋友或者自己有自己的代碼書寫風格的人,可能在【數據庫表的字段名】用的是xxx_xxx,然后在【Java的實體類的成員變量的名】用的是xxxXxx,然后因為二者名字不一樣而導致數據沒有對應封裝
解決辦法:
1、(個人首選推薦):在application.properties配置里加下面的代碼
它會自動把【xxx_xxx】轉換映射成【xxXxx】,很方便(前提必須嚴格按要求:數據庫起名是xx_xxx、java這邊起名是駝峰形xxXxx)
#開啟mybatis的駝峰命名自動映射開關
mybatis.configuration.map-underscore-to-camel-case = true
2、(個人次推薦):咱就老老實實跟數據庫表里的字段名都一樣不就完事了嘛.......
3、(略麻煩):通過Results、Result注解手動映射封裝
4、(巨麻煩):sql里的起別名的方法,在sql語句里把 * 換成寫出所有字段名,然后名字不同的那幾個在后面空格、再跟上別名,別名就是【Java的實體類的成員變量的名】
6、略復雜的查詢
現在實現這么一個功能,根據姓名、性別、入職時間范圍來組合條件查詢
要求是:
? ? ? ? 名字里含有什么字啥?
????????性別是啥?
????????入職時間在(時間范圍)之間
????????并要求按 “員工信息的更新時間” 來倒序排序顯示
例子:
? ? ? ? 名字里含有“張”字
????????性別是男(1)
????????入職時間在(2010年1月1日 — 2020年1月1日)之間
????????并要求按 “員工信息的更新時間” 來倒序排序顯示
那么在數據庫sql是這樣寫
然后注意一下,這里sql里模糊查詢條件like后面必須跟“字符串”,然后要想動態傳值就得#{變量}
但是!!!#{變量}產生的預編譯語句的“?”不能被包在字符串里!!!
那么有的教程就會教你用另一個“${變量}”,因為“${變量}”是直接拼接字符串嘛
但是要注意,單純像下圖這么寫的話,老子試了半個小時越試越特么煩,渾身刺撓,
因為黑馬程序員這個老畢登瞎鉤八講,在視頻最后才提到說如果要傳遞多個參數,說什么springBoot 1.x版本要用【@Param(“變量”)】這個注解來指定#{ 變量 }的變量是下面函數里的參數
放他娘狗屁!早又不說非要老子查他媽半年bug,然后說的還是錯的
記住了!!!不管你是哪個版本,哪怕是昨天spring公司開更新的最新版,只要Mapper接口的方法要傳遞多個參數時,都給我加上【@Param(“變量”)】在每一個參數前面!!!!!
ok,那現在還有一個問題,不是說${ }容易被攻擊嗎?想用#{ }?也可以
直接用這個方法:sql里用 【concat()方法】 可以拼接字符串!!!!
那么Mybatis這邊這樣寫
成功
案例中完整的Mybatis代碼如下
Mapper目錄下EmpMapper的所有執行sql的操作接口代碼
//這塊別復制,你們自己有自己的路徑
//package com.czm.mybatis01.mapper;//這塊別復制,你們自己有自己定義的類的位置
//import com.czm.mybatis01.table.Emp;import org.apache.ibatis.annotations.*;
import org.springframework.beans.factory.annotation.Autowired;import java.time.LocalDate;
import java.util.List;@Mapper
public interface EmpMapper {//@Sql的注解( "要執行的sql語句" )@Delete("delete from emp where id = ${id}")//對應這個sql語句執行的一些相關方法public void delete(Integer 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},#{dept_id},#{create_time},#{update_time})" )public void insert(Emp emp);@Options( useGeneratedKeys = true, keyProperty = "id")@Update(" update emp set username = #{username}, name = #{name}, gender = #{gender}, image = #{image}," +"job = #{job}, entrydate = #{entrydate}, dept_id = #{dept_id}, update_time = #{update_time} where id = #{id}; ")public void Update(Emp emp);//批量查詢所有信息@Select( "select * from emp" )public List<Emp> selectAll();//根據id查詢員工信息@Select( "select * from emp where id = #{id}" )public Emp select(Integer id);//復雜查詢員工
// @Select( "select * from emp where" +
// " name like '%${name}%' " +
// " and gender = #{gender}" +
// " and entrydate between #{begin} and #{end}" +
// " order by update_time desc" )@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> DifficultSelect(@Param("name") String name, @Param("gender") Short gender, @Param("begin") LocalDate begin, @Param("end") LocalDate end);}
test目錄下的ApplicationTest測試文件的代碼:
//這塊別復制,你們自己有自己的路徑
//package com.czm.mybatis01;//這塊別復制,你們自己有自己定義的接口的位置
//import com.czm.mybatis01.mapper.EmpMapper;
//import com.czm.mybatis01.mapper.UsrMapper;
//import com.czm.mybatis01.table.Emp;
//import com.czm.mybatis01.table.User;import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;@SpringBootTest //springboot整合單元測試的注解
class Mybatis01ApplicationTests {//使用@Autowired注解可以【依賴注入】,直接創建UserMapper接口的實例化對象//可以理解為跳過了【創建實現接口類】這一步,spring boot幫我們創建好了一個@Autowiredprivate EmpMapper empMapper;@Testpublic void testDelete(){//我這里為了簡單測試,就當是前端已經把id傳過來了,就是17,直接把實際參數傳進去empMapper.delete(17);}@Testpublic void testInsert(){//這樣就錯了!!我們在接口定義的參數是一整個對象,不是這么零散的參數//empMapper.insert("CZM","岑梓銘",(short)1,"1.jpg",(short)3, LocalDate.of(2024,1,1),1, LocalDateTime.now(),LocalDateTime.now());//得先構建一個對象,然后把對應的值調用setter方法設置//對象里沒用的成員變量就不用設置,不設置的變量就是默認值,也不會報錯也不會有影響Emp emp1 = new Emp();emp1.setJob((short)3);emp1.setGender((short)2);emp1.setUsername("CYH");emp1.setName("蔡勇豪");emp1.setImage("19.jpg");emp1.setEntrydate(LocalDate.of(2024,1,1));emp1.setDept_id(1);emp1.setCreate_time(LocalDateTime.now());emp1.setUpdate_time(LocalDateTime.now());empMapper.insert(emp1);System.out.println(emp1.getId());}@Testpublic void testUpdate(){Emp emp2 = new Emp();emp2.setId(1);emp2.setName("王大陸");emp2.setUsername("WDL");emp2.setGender((short)1);emp2.setImage("19.jpg");emp2.setJob((short)2);emp2.setEntrydate(LocalDate.of(2024,1,3));emp2.setDept_id(2);emp2.setUpdate_time(LocalDateTime.now());empMapper.Update(emp2);}@Testpublic void testSelect(){empMapper.selectAll();empMapper.select(8);}@Testpublic void TestDifficultSelect(){List<Emp> list = empMapper.DifficultSelect("張", (short)1, LocalDate.of(2010,1,1), LocalDate.of(2020,1,1));System.out.println(list);}
}