🔥博客主頁:?【小扳_-CSDN博客】
?感謝大家點贊👍收藏?評論?
文章目錄
????????1.0 Mybatis 的基礎操作
????????2.0 基礎操作 - 環境準備
????????3.0 基礎操作 - 刪除操作
????????3.1 SQL 預編譯
? ? ? ? 3.2 SQL 預編譯的優勢
? ? ? ? 3.3 參數占位符
????????4.0 基礎操作 - 新增
? ? ? ? 4.1 主鍵返回
????????5.0 基礎操作 - 更新
????????6.0 基礎操作 - 查詢
????????6.1 根據 ID 來查詢
? ? ? ? 6.2 數據封裝
????????6.3 根據條件查詢
????????7.0 本篇文章的完整代碼
????????7.1 application.properties 屬性文件
????????7.2 pom.xml 文件
????????7.3 Mapper 接口
????????7.4 測試代碼
????????7.5 Emp 類
? ? ? ? 1.0 Mybatis 的基礎操作
????????Mybatis 是一個基于 Java 的持久層框架,它可以幫助開發者簡化數據庫操作。
? ? ? ? 下面是 Mybatis 的基礎操作:查詢操作、新增操作、更新操作、刪除操作。通過以上基礎的操作,開發者可以使用 Mybatis 進行數據庫操作,實現數據的增刪改查功能。
? ? ? ? 2.0 基礎操作 - 環境準備
? ? ? ? 1)準備數據庫表 emp
? ? ? ? 2)創建一個新的 Springboot 工程,選擇引入對應的起步依賴(mybatis、mysql 驅動包、lombok 工具包)
<!--mybatis的起步依賴--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.2</version></dependency><!--mysql驅動包--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency>
? ? ? ? 3)application.properties 中引入數據庫連接信息
spring.datasource.drive-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/db01?characterEncoding=utf-8&useSSL=true spring.datasource.username=root spring.datasource.password=123456
? ? ? ? 4)創建對應的實體類 Emp
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;import java.time.LocalDate; import java.time.LocalDateTime;@Data @NoArgsConstructor @AllArgsConstructor public class Emp {private int id;private String username;private String password;private String name;private int gender;private int job;private LocalDate entrydate;private int deptId;private LocalDateTime createTime;private LocalDateTime updateTime;}
? ? ? ? 5)準備 Mapper 接口 EmpMapper
? ? ? ? 基礎操作的抽象方法都寫在該接口中,該接口用 @Mapper 注解。
? ? ? ? 3.0 基礎操作 - 刪除操作
? ? ? ? 在 Mapper 接口中定義一個 delete() 的抽象方法,且在該方法上加上 @Delete("刪除的 SQL 語句") 注解,只要實現類調用重寫的 delete() 方法時,就會自動執行 SQL 語句。
import org.apache.ibatis.annotations.*;import java.time.LocalDate; import java.util.List;@org.apache.ibatis.annotations.Mapper public interface Mapper {//刪除數據操作@Delete("delete from emp where id = #{id}")public void delete(int id);}
? ? ? ? SQL 語句:根據 ID 來刪除數據。#{} 是 mybatis 中的占位符,當實體類調用 delete 方法的時候,方法中的參數 id 自動賦值給 #{id} 占位符中的 id 。
測試刪除數據操作:
@SpringBootTest public class AppTest extends TestCase {@AutowiredMapper deleteMapper;@Testpublic void testDelete(){deleteMapper.delete(3);}}
? ? ? ? 通過 @Autowired 注解根據類型注入到?deleteMapper 變量中,再由 deleteMapper 調用 delete() 方法,就可以自動執行 SQL 語句,且方法參數也會自動賦值給 #{id} 。
測試結果:
初始的 emp:
測試后的 emp:
? ? ? ? 注意事項:如果 mapper 接口方法形參只有一個普通類型的參數,#{...} 里面的屬性名可以隨便寫,如:#{id}、#{value} 。
????????3.1 SQL 預編譯
????????SQL 預編譯是在執行 SQL 語句之前,將 SQL 語句編譯成可執行的硬編碼形式,并在執行時直接使用編譯好的代碼,從而減少了每次執行 SQL 語句時的語法解析和優化等過程,提高了 SQL 語句執行的效率。
????????預編譯 SQL 語句的過程主要包括 SQL 語法解析、語義分析、查詢優化和生成可執行代碼等步驟。
????????在執行 SQL 語句之前,先將 SQL 語句傳遞給數據庫驅動程序進行預編譯,然后再執行預編譯好的 SQL 語句。
? ? ? ? 3.2 SQL 預編譯的優勢
? ? ? ? 在發送給數據庫之前,會先進行一次 SQL 預編譯,將 #{id} 占位符變為 '?' ,再將方法中的參數 id 一起發送給數據庫進行運行操作,而不是直接將拼接好的 SQL 語句發送給數據庫。
? ? ? ? 1)性能更高
? ? ? ? 采取 SQL 預編譯,可以提升性能。
? ? ? ? 對于?SQL 預編譯的方式來說,由于 #{id} 無論 id 為何值都會變為 '?' ,所以相同的 SQL 語句只需要編譯一次就夠了,因為內存中已經有相同的 SQL 語句了。預編譯好的 SQL 連同參數 id 一起發送到數據庫中。
? ? ? ? 對于 SQL 直接拼接參數的方式來說,簡單來說,將 SQL “寫死”了,每一次的 id 大概率都是不一樣的,那么每一條 SQL 語句都要進行SQL 語法解析、語義分析、查詢優化和生成可執行代碼等步驟。因此采取 SQL 預編譯的方式,性能會更好。
? ? ? ? 2)更安全(防止 SQL 注入)
????????預編譯語句可以將 SQL 查詢語句和參數分開,從而避免用戶輸入的數據直接被拼接到 SQL 語句中。
? ? ? ? 對于 SQL 語句直接與參數拼接,可能會存在 SQL 注入。舉個例子:
?????????
SELECT * FROM users WHERE username='admin' AND password='' OR '1'='1';
? ? ? ? 在登錄密碼的時候,密碼寫成:' OR '1'='1 ,再將其直接拼接到 SQL 語句中,就會導致以上情況 '1' = '1' 恒成立,因此就可以繞過登錄驗證,從而直接登錄。
????????上述 SQL 查詢語句將返回數據庫中的所有用戶數據,導致登錄成功,即使用戶輸入的密碼是錯誤的。這就是 SQL 注入攻擊,利用用戶輸入的惡意代碼來繞過登錄驗證。要防止這種情況發生,可以使用預編譯語句或參數化查詢來避免直接拼接用戶輸入數據到 SQL 查詢語句中。
? ? ? ? 而對于 SQL 預編譯來說:不存在 SQL 注入攻擊,因為 SQL 語句和參數分開了,沒有直接拼接到 SQL 語句中。
? ? ? ? 3.3 參數占位符
? ? ? ? 1)#{...}:執行 SQL 時,會將 #{...} 替換為 ? ,生成預編譯 SQL ,會自動設置參數值。使用時機:參數傳遞,都使用 #{...} 。
在編寫 SQL 語句中使用 #{} 這個占位符,就會自動進行 SQL 預編譯,將 #{id} 就會被?'?' 替代掉。
? ? ? ? 2)${...}:拼接 SQL ,直接將參數拼接在 SQL 語句中,存在 SQL 注入問題。使用時機:如果對表名、列表進行動態設置時使用。
? ? ? ? 4.0 基礎操作 - 新增
? ? ? ? 在 Mapper 接口中定義 insert() 抽象方法,且在該方法加上 @Insert("新增的 SQL 語句")
代碼演示:
import org.apache.ibatis.annotations.*;import java.time.LocalDate; import java.util.List;@org.apache.ibatis.annotations.Mapper public interface Mapper {//增加數據操作@Insert("insert into emp(username,name,gender,job,entrydate,dept_id,create_time,update_time) " +"values (#{username},#{name},#{gender},#{job},#{entrydate},#{deptId},#{createTime},#{updateTime})")public void insert(Emp emp);}
? ? ? ? 當實體類調用該重寫的 insert() 方法,就會自動執行該 SQL 語句,由于參數比較多,可以用對象進行封裝起來,用 emp 作為參數,emp 中的字段都會一一對應的賦值到 SQL 語句中 #{} 占位符中,占位符中的變量名需要與 emp 中的字段保持一致,才能進行正常的賦值操作。
測試新增操作:
@SpringBootTest public class AppTest extends TestCase {@AutowiredMapper mapper;@Testpublic void testInsert(){Emp emp = new Emp();emp.setUsername("小板");emp.setName("XB");emp.setJob(1);emp.setEntrydate(LocalDate.of(2024,3,4));emp.setCreateTime(LocalDateTime.now());emp.setUpdateTime(LocalDateTime.now());emp.setDeptId(1);mapper.insert(emp);}}
? ? ? ? 先通過 @Autowire 注解,將新增對象注入給 mapper 變量名。創建一個新的 emp 對象,將其字段進行賦值后,再將 emp 以參數的形式交給?#{} 占位符中的變量。
測試結果:
新增之前的數據庫:
新增之后的數據庫:
? ? ? ? 4.1 主鍵返回
? ? ? ? 在數據添加成功后,需要獲取插入數據庫數據的主鍵。
? ? ? ? 只需在 @Insert 注解上再添加一個 @Options(useGeneratedKeys = true,keyProperty = "id")?注解,其中?useGeneratedKeys = true 代表:數據添加成之后,需要返回主鍵。keyProperty = "id" 代表:返回的主鍵交給 id 變量。
代碼演示:
import org.apache.ibatis.annotations.*;import java.time.LocalDate; import java.util.List;@org.apache.ibatis.annotations.Mapper public interface Mapper {@Options(useGeneratedKeys = true,keyProperty = "id")//增加數據操作@Insert("insert into emp(username,name,gender,job,entrydate,dept_id,create_time,update_time) " +"values (#{username},#{name},#{gender},#{job},#{entrydate},#{deptId},#{createTime},#{updateTime})")public void insert(Emp emp);}
@SpringBootTest public class AppTest extends TestCase {@AutowiredMapper mapper;@Testpublic void testInsert(){Emp emp = new Emp();emp.setUsername("小童");emp.setName("TT");emp.setJob(0);emp.setEntrydate(LocalDate.of(2024,3,4));emp.setCreateTime(LocalDateTime.now());emp.setUpdateTime(LocalDateTime.now());emp.setDeptId(1);mapper.insert(emp);System.out.println(emp.getId());}}
運行結果:
? ? ? ? 這樣就拿到了主鍵,也稱為主鍵返回。
????????5.0 基礎操作 - 更新
? ? ? ? 在 Mapper 接口中定義一個 update(參數) 抽象方法,且在該方法加上 @Update("更新 SQL 語句") 注解,當實體類調用重寫的 update(參數) 方法時,就會自動執行 SQL 語句,且方法中的參數就會自動賦值給 #{} 占位符。
代碼演示:
import org.apache.ibatis.annotations.*;import java.time.LocalDate; import java.util.List;@org.apache.ibatis.annotations.Mapper public interface Mapper {//更新數據操作@Update("update emp set username = #{username} , name = #{name} , gender = #{gender} , job = #{job}," +"entrydate = #{entrydate} , dept_id = #{deptId}, create_time = #{createTime}, update_time = #{updateTime} where id = #{id}")public void update(Emp emp);}
? ? ? ? 需要注意的是, emp 中的字段名必須要跟 #{} 占位符中的變量名保持相同,才能進行正常的賦值操作。
測試更新操作:
@SpringBootTest public class AppTest extends TestCase {@AutowiredMapper updateMapper;@Testpublic void testUpdate(){Emp emp = new Emp();emp.setId(5);emp.setUsername("小扳手");emp.setName("TT");emp.setJob(3);emp.setEntrydate(LocalDate.of(2022,3,5));emp.setCreateTime(LocalDateTime.now());emp.setUpdateTime(LocalDateTime.now());emp.setDeptId(1);mapper.update(emp);}}
更新之前:
更新之后:
????????6.0 基礎操作 - 查詢
? ? ? ? 在 Mapper 接口中定義一個 select(參數) 抽象方法,且在該抽象方法加上 @Select("查詢 SQL 的語句"),實體類調用重寫的 select(參數) 方法時,就自動執行 SQL 語句,且查詢出來的數據可以用 Emp 實體對象來接收。
????????6.1 根據 ID 來查詢
代碼演示:
import org.apache.ibatis.annotations.*;import java.time.LocalDate; import java.util.List;@org.apache.ibatis.annotations.Mapper public interface Mapper {//根據ID查詢@Select("select * from emp where id = #{id}")public Emp selectID(int id);}
測試根據 ID 查詢:?
@SpringBootTest public class AppTest extends TestCase {@AutowiredMapper selectMapper;@Testpublic void testSelect(){Emp emp = selectMapper.selectID(1);System.out.println(emp);}}
????????首先通過 @Autowired 注解,將 Mapper 類型的對象注入給 selectMapper 對象,再調用 selectID(1) 方法,代表查詢 ID 為 1 的數據。接著查詢的結果交給?emp 。
運行結果:
? ? ? ? 6.2 數據封裝
????????實體類屬性名和數據庫表查詢返回的字段名一致,mybatis 會自動封裝;如果實體類屬性名和數據庫查詢返回的字段名不一致,不能自動分裝。
解決屬性名與字段名不一致的方法:
? ? ? ? 1)給字段起別名,讓別名與實體類屬性名保持一致。
? ? ? ? 2)通過 @Results,@Result 注解手動映射封裝
代碼演示:
import org.apache.ibatis.annotations.*;import java.time.LocalDate; import java.util.List;@org.apache.ibatis.annotations.Mapper public interface Mapper {//手動建立映射關系@Results({//column 代表原來的字段名、property 代表替換的字段名@Result(column = "create_time",property = "createTime"),@Result(column = "dept_id",property = "deptId"),@Result(column = "update_time",property = "updateTime")})//根據ID查詢@Select("select * from emp where id = #{id}")public Emp selectID(int id);}
? ? ? ? 3)開啟 mybatis 的駝峰命名自動映射開關(create_Time ---->?createTime)
? ? ? ? 在 application.properties 屬性文件中配置:
#開啟 mybatis 的駝峰命名自動映射開關 mybatis.configuration.map-underscore-to-camel-case=true
????????6.3 根據條件查詢
????????條件:根據輸入的員工姓名、員工性別、入職時間搜索滿足條件的員工信息。其中員工姓名,支持模糊匹配;性別進行精確查詢;入職時間進行范圍查詢;并對查詢的結果,根據最后修改時間進行倒敘排序。
代碼演示:
import org.apache.ibatis.annotations.*;import java.time.LocalDate; import java.util.List;@org.apache.ibatis.annotations.Mapper public interface Mapper {//根據條件進行查詢@Select("select * from emp where name like concat('%',#{name},'%') and gender = #{gender} and " +"entrydate between #{start} and #{end} order by update_time desc ")public List<Emp> selectCondition(String name, int gender, LocalDate start,LocalDate end);}
? ? ? ? 這里用到了 concat() 方法用于連接兩個或多個字符串的方法。它將指定的字符串與調用字符串連接,并將返回一個新的字符串,而不會更改原始字符串。這個方法可以接受任意數量的參數,然后將它們連接在一起,以創建一個新的字符串。
? ? ? ? 注意事項:#{} 占位符不能內嵌在 "" 中,而 ${} 占位符可以內嵌在 "" 中。
????????7.0 本篇文章的完整代碼
????????7.1 application.properties 屬性文件
spring.datasource.drive-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/db01?characterEncoding=utf-8&useSSL=true spring.datasource.username=root spring.datasource.password=123456#將日志打印到控制臺上 mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl#開啟駝峰命名自動映射開關 mybatis.configuration.map-underscore-to-camel-case=true
????????7.2 pom.xml 文件
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>code_24_5_20_2</artifactId><version>1.0-SNAPSHOT</version><packaging>jar</packaging><name>code_24_5_20_2</name><url>http://maven.apache.org</url><!--1、spring-boot-starter-parent自動引入springboot中最基礎的組件,所有springboot項目都要依賴它進行構建--><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.4</version></parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><!--2、引入springboot依賴,spring-boot-starter-web表示在項目中增加支持javaweb的功能,版本信息已經在parent中定義--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope></dependency><!--mybatis的起步依賴--><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.2</version></dependency><!--mysql驅動包--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!--lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><!--Druid依賴--><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.8</version></dependency></dependencies><!--3、定義springboot的打包方式,spring-boot-maven-plugin可以在打包時自動將所有類和資源打包成一個獨立可運行的jar包--><build><!--打包指定名稱--><finalName>projectName</finalName><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
????????7.3 Mapper 接口
import org.apache.ibatis.annotations.*;import java.time.LocalDate; import java.util.List;@org.apache.ibatis.annotations.Mapper public interface Mapper {//根據條件進行查詢@Select("select * from emp where name like concat('%',#{name},'%') and gender = #{gender} and " +"entrydate between #{start} and #{end} order by update_time desc ")public List<Emp> selectCondition(String name, int gender, LocalDate start,LocalDate end);//手動建立映射關系@Results({//column 代表原來的字段名、property 代表替換的字段名@Result(column = "create_time",property = "createTime"),@Result(column = "dept_id",property = "deptId"),@Result(column = "update_time",property = "updateTime")})//根據ID查詢@Select("select * from emp where id = #{id}")public Emp selectID(int id);//更新數據操作@Update("update emp set username = #{username} , name = #{name} , gender = #{gender} , job = #{job}," +"entrydate = #{entrydate} , dept_id = #{deptId}, create_time = #{createTime}, update_time = #{updateTime} where id = #{id}")public void update(Emp emp);@Options(useGeneratedKeys = true,keyProperty = "id")//增加數據操作@Insert("insert into emp(username,name,gender,job,entrydate,dept_id,create_time,update_time) " +"values (#{username},#{name},#{gender},#{job},#{entrydate},#{deptId},#{createTime},#{updateTime})")public void insert(Emp emp);//刪除數據操作@Delete("delete from emp where id = #{id}")public void delete(int id);}
????????7.4 測試代碼
import junit.framework.TestCase; import junit.framework.TestSuite; import org.example.CURD.Emp; import org.example.CURD.Mapper; import org.example.Project.GetData; import org.example.Project.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;/*** Unit test for simple App.*/ @SpringBootTest public class AppTest extends TestCase {@AutowiredMapper selectMapper;@Testpublic void testSelect(){Emp emp = selectMapper.selectID(1);System.out.println(emp);}@AutowiredMapper updateMapper;@Testpublic void testUpdate(){Emp emp = new Emp();emp.setId(5);emp.setUsername("小扳手");emp.setName("TT");emp.setJob(3);emp.setEntrydate(LocalDate.of(2022,3,5));emp.setCreateTime(LocalDateTime.now());emp.setUpdateTime(LocalDateTime.now());emp.setDeptId(1);mapper.update(emp);}@AutowiredMapper mapper;@Testpublic void testInsert(){Emp emp = new Emp();emp.setUsername("小童");emp.setName("TT");emp.setJob(0);emp.setEntrydate(LocalDate.of(2024,3,4));emp.setCreateTime(LocalDateTime.now());emp.setUpdateTime(LocalDateTime.now());emp.setDeptId(1);mapper.insert(emp);System.out.println(emp.getId());}@AutowiredMapper deleteMapper;@Testpublic void testDelete(){deleteMapper.delete(3);}@AutowiredGetData get;@Testpublic void test(){List<User> list = get.getData();list.forEach(System.out::println);}@AutowiredMapper selectCondition;@Testpublic void testSelectCondition(){List<Emp> list = selectCondition.selectCondition("童",0,LocalDate.of(2022,3,5),LocalDate.of(2025,4,4));System.out.println(list);} }
????????7.5 Emp 類
import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;import java.time.LocalDate; import java.time.LocalDateTime;@Data @NoArgsConstructor @AllArgsConstructor public class Emp {private int id;private String username;private String password;private String name;private int gender;private int job;private LocalDate entrydate;private int deptId;private LocalDateTime createTime;private LocalDateTime updateTime;}