我 | 在這里
🕵? 讀書 | 長沙 ?軟件工程 ? 本科
🏠 工作 | 廣州 ? Java 全棧開發(軟件工程師)
🎃 愛好 | 研究技術、旅游、閱讀、運動、喜歡流行歌曲
🏷? 標簽 | 男 自律狂人 目標明確 責任心強
??公眾號 | 熱愛技術的小鄭
🚀 郵箱 | 2977429967@qq.com
?? GitHub項目倉庫 開源項目 + 實戰Demo
?
為何而寫?
🍍 好記性不如爛筆頭,記錄學習的相關知識 、項目 BUG 解決
🍇 復盤總結,加深記憶,方便自己查看
🍑 分享知識,咱就是這么樂于助人、專注填坑20年、哈哈哈哈
?
目標描述
🏆 沒有傘的孩子、只能用力奔跑。向著架構師的方向努力、做一個有始有終的人。
所有源碼公布開源在GitHub 上:Mybatsi 案例源碼傳送門
1、Mybatis簡介
1.1 什么是Mybatis
如何獲得Mybatis
● maven倉庫:
<dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.6</version></dependency>
● 中文文檔:https://mybatis.org/mybatis-3/zh/index.html
● Github:https://github.com/mybatis/mybatis-3
1.2 持久化
數據持久化
● 持久化就是將程序的數據在持久狀態和瞬時狀態轉化的過程
● 內存:斷電即失
● 數據庫(Jdbc),io文件持久化
● 生活方面例子:冷藏,罐頭。
為什么需要持久化?
● 不想丟掉一些對象
● 內存太貴
1.3 持久層
Dao層,Service層,Controller層…
● 完成持久化工作的代碼塊
● 層界限十分明顯
1.4 為什么需要Mybatis?
● 幫助程序猿將數據存入到數據庫中
● 方便
● 傳統的JDBC代碼太復雜,簡化–>框架–>自動化
2、第一個Mybatis程序
2.1 搭建環境
整體過程
● 1、新建一個maven項目
● 2、刪除src目錄
● 3、pom中導入相關依賴(可選)
● 4、新建一個module、目的方便項目擴展
● 5、子項目中也可以導入相關依賴(建議導入子項目,方便區分不同的module)
<dependencies><!--mysql driver--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.9</version></dependency><!--mybatis--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.6</version></dependency><!--junit--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency></dependencies>
重點提示 :需要加上如下代碼。否則部分代碼無法編譯道target 目錄下,導致運行時報錯
<build><!--**.xml寫在src找不到問題解決方案 --><resources><resource><!-- directory:指定資源文件的位置 --><directory>src/main/java</directory><includes><!-- “**” 表示任意級目錄 “*”表示任意任意文件 --><!-- mvn resources:resources :對資源做出處理,先于compile階段 --><include>**/*.properties</include><include>**/*.xml</include></includes><!-- filtering:開啟過濾,用指定的參數替換directory下的文件中的參數(eg. ${name}) --><filtering>false</filtering></resource><resource><directory>src/main/resources</directory></resource></resources></build>
2.2 創建模塊
● 編寫mybatis的核心配置文件 (這里的mappers 以及起別名 可以后期編寫實體類、mapper的相關代碼后再寫)
<?xml version="1.0" encoding="UTF-8" ?><environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/spring_test?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8&useSSL=false"/><property name="username" value="root"/><property name="password" value="root"/></dataSource></environment>
</environments><!--加載映射配置文件-->
<mappers><mapper resource="com/zyz/mybatis/mapper/StudentMapper.xml"/>
</mappers>
● 編寫mybatis 的相關工具類
package com.zyz.mybatis.entity;/*** @BelongsProject: SpringDemo1* @BelongsPackage: com.zyz.SpringDemo2.entity* @Author: zhengyuzhu* @CreateTime: 2023-11-16 22:33* @Description: TODO* @Version: 1.0*/
public class Student {private int stuNo;private String stuName;private int cardID;private int classID;public Student() {}public Student(int stuNo, String stuName, int cardID, int classID) {this.stuNo = stuNo;this.stuName = stuName;this.cardID = cardID;this.classID = classID;}public int getStuNo() {return stuNo;}public void setStuNo(int stuNo) {this.stuNo = stuNo;}public String getStuName() {return stuName;}public void setStuName(String stuName) {this.stuName = stuName;}public int getCardID() {return cardID;}public void setCardID(int cardID) {this.cardID = cardID;}public int getClassID() {return classID;}public void setClassID(int classID) {this.classID = classID;}@Overridepublic String toString() {return "Student{" +"stuNo=" + stuNo +", stuName='" + stuName + '\'' +", cardID=" + cardID +", classID=" + classID +'}';}
}
2.3 編寫代碼
實體類 Student
package com.zyz.mybatis.entity;/*** @BelongsProject: SpringDemo1* @BelongsPackage: com.zyz.SpringDemo2.entity* @Author: zhengyuzhu* @CreateTime: 2023-11-16 22:33* @Description: TODO* @Version: 1.0*/
public class Student {private int stuNo;private String stuName;private int cardID;private int classID;public Student() {}public Student(int stuNo, String stuName, int cardID, int classID) {this.stuNo = stuNo;this.stuName = stuName;this.cardID = cardID;this.classID = classID;}public int getStuNo() {return stuNo;}public void setStuNo(int stuNo) {this.stuNo = stuNo;}public String getStuName() {return stuName;}public void setStuName(String stuName) {this.stuName = stuName;}public int getCardID() {return cardID;}public void setCardID(int cardID) {this.cardID = cardID;}public int getClassID() {return classID;}public void setClassID(int classID) {this.classID = classID;}@Overridepublic String toString() {return "Student{" +"stuNo=" + stuNo +", stuName='" + stuName + '\'' +", cardID=" + cardID +", classID=" + classID +'}';}
}
mapper接口 StudentMapper 及 xml配置文件 StudentMapper.xml
/*** @author zyz* @version 1.0* @data 2023/11/16 22:33* @Description:*/
public interface StudentMapper {/*** @description: 查詢全部* @author: zhengyuzhu* @date: 2023/11/16 22:34* @return: java.util.List<com.zyz.SpringDemo2.entity.Student>**/List<Student> queryAll();/*** @description: 新增學生* @author: zhengyuzhu* @date: 2023/11/16 22:34* @param: student**/void addStudent(Student student);
}
StudentMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zyz.mybatis.mapper.StudentMapper"><!--查詢全部--><select id="queryAll" resultType="Student">select * from student</select><!--新增學生--><insert id="addStudent" parameterType="Student">insert into student (stuno,stuname,cardid,classid)values (#{stuNo},#{stuName},#{cardID},#{classID})</insert>
</mapper>
2.4 測試單元
/*** @description: 基本測試* @author: zhengyuzhu* @date: 2023/11/20 22:21**/
@Test
public void test01(){//第一步:獲得SqlSession對象SqlSession sqlSession = MybatisUtils.getSqlSession();//方式一:getMapperStudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);List<Student> userList = studentMapper.queryAll();for (Student student : userList) {System.out.println(student);}//關閉SqlSessionsqlSession.close();/*** 輸出如下 :** Student{stuNo=1, stuName='張三', cardID=1115, classID=1}* Student{stuNo=2, stuName='李四', cardID=1116, classID=2}* Student{stuNo=3, stuName='王五', cardID=1117, classID=3}***/}
3、測試增刪改查(CURD)
提示:我們的實體類,或者數據庫中的表,字段或者參數過多,我們應當考慮使用Map!具體案例參考 3.3 修改。修改用戶信息的時候,使用了兩種方式,一種是通過對象。另外一種是通過map 。有關參數的賦值以及sql語句的字段 獲取。都有說明
3.1 添加
mapper 和 xml 配置文件
/*** @description: 新增學生* @author: zhengyuzhu* @date: 2023/11/16 22:34* @param: student** @return*/Integer addStudent(Student student);<!--新增學生--><insert id="addStudent" parameterType="Student">insert into student (stuno, stuname, cardid, classid)values (#{stuNo}, #{stuName}, #{cardID}, #{classID})</insert>
測試單元
/*** @description: 添加對象* @author: zhengyuzhu* @date: 2023/11/21 21:06**/@Testpublic void test04(){//第一步:獲得SqlSession對象SqlSession sqlSession = MybatisUtils.getSqlSession();//方式一:getMapperStudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);Student student = new Student();student.setStuNo(9);student.setStuName("武松");student.setClassID(8);student.setCardID(8);studentMapper.addStudent(student);sqlSession.commit();//將數據保存到數據庫Student studentRs = studentMapper.queryStudentById(9);System.out.println(studentRs);//關閉SqlSessionsqlSession.close();/*** 輸出如下 :* Student{stuNo=9, stuName='武松', cardID=8, classID=8}***/}
3.2 刪除
mapper 和 xml 配置文件
/*** @description:刪除學生 通過 學號* @author: zhengyuzhu* @date: 2023/11/20 22:45* @param: stuNo* @return: java.lang.Integer**/Integer deleteStudentById(Integer stuNo);<delete id="deleteStudentById">deletefrom studentwhere stuNo = #{stuNo}</delete>
測試單元
/*** @description: 刪除對象* @author: zhengyuzhu* @date: 2023/11/21 21:07**/@Testpublic void test05(){//第一步:獲得SqlSession對象SqlSession sqlSession = MybatisUtils.getSqlSession();StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);studentMapper.deleteStudentById(9);sqlSession.commit();//將數據保存到數據庫Student studentRs = studentMapper.queryStudentById(9);System.out.println(studentRs);//關閉SqlSessionsqlSession.close();/*** 輸出如下 :* null***/}
3.3 修改
mapper 和 xml 配置文件
一個是通過對象的方式修改,一個是通過 map
/** * @description: 修改學生信息* @author: zhengyuzhu* @date: 2023/11/20 22:45* @param: student* @return: java.lang.Integer**/Integer updateStudent(Student student);/*** @description: 通過Map 修改學生信息* @author: zhengyuzhu* @date: 2023/11/21 21:08* @param: studentMap* @return: java.lang.Integer**/Integer updateStudentMap(HashMap<String,Object> studentMap);<update id="updateStudent" parameterType="Student">update studentset stuNo = #{stuNo},stuName = #{stuName},cardid = #{cardID},classid = #{classID}where stuNo = #{stuNo}</update><update id="updateStudentMap" parameterType="map">update studentset stuNo = #{stuNo},stuName = #{stuName},cardid = #{cardid},classid = #{classid}where stuNo = #{stuNo}</update>
測試單元
/*** @description: 修改對象信息 通過Map* @author: zhengyuzhu* @date: 2023/11/20 23:02**/@Testpublic void test02(){//第一步:獲得SqlSession對象SqlSession sqlSession = MybatisUtils.getSqlSession();//方式一:getMapperStudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);HashMap<String, Object> stuMap = new HashMap<>();stuMap.put("stuNo",1);stuMap.put("stuName","李白");stuMap.put("cardid",6);stuMap.put("classid",6);Integer rs = studentMapper.updateStudentMap(stuMap);Student student = studentMapper.queryStudentById(1);System.out.println(student);//關閉SqlSessionsqlSession.close();/*** 輸出如下 :** Student{stuNo=1, stuName='張三', cardID=1115, classID=1}* Student{stuNo=2, stuName='李四', cardID=1116, classID=2}* Student{stuNo=3, stuName='王五', cardID=1117, classID=3}***/}/*** @description: 修改對象信息 通過 Student 對象* @author: zhengyuzhu* @date: 2023/11/20 23:02**/@Testpublic void test03(){//第一步:獲得SqlSession對象SqlSession sqlSession = MybatisUtils.getSqlSession();//方式一:getMapperStudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);Student studentBefore = studentMapper.queryStudentById(0);System.out.println("修改前的學生信息 : " + studentBefore);Student student = new Student();student.setStuNo(0);student.setStuName("麻子嘞");student.setClassID(6);student.setCardID(6);studentMapper.updateStudent(student);sqlSession.commit();//將數據保存到數據庫Student studentAfter = studentMapper.queryStudentById(0);System.out.println("修改后的學生信息 : " + studentAfter);//關閉SqlSessionsqlSession.close();/*** 輸出如下 :** 修改前的學生信息 : Student{stuNo=0, stuName='李四', cardID=8, classID=8}* 修改后的學生信息 : Student{stuNo=0, stuName='麻子嘞', cardID=6, classID=6}***/}
3.4 查詢
mapper 和 xml 配置文件
/*** @description: 查詢全部* @author: zhengyuzhu* @date: 2023/11/16 22:34* @return: java.util.List<com.zyz.SpringDemo2.entity.Student>**/List<Student> queryAll();/*** @description:刪除學生 通過 學號* @author: zhengyuzhu* @date: 2023/11/20 22:45* @param: stuNo* @return: java.lang.Integer**/Integer deleteStudentById(Integer stuNo);<!--查詢全部--><select id="queryAll" resultType="Student">select *from student</select><select id="queryStudentById" resultType="com.zyz.mybatis.entity.Student">select *from studentwhere stuNo = #{stuNo}</select>
測試單元
/*** @description: 基本測試* @author: zhengyuzhu* @date: 2023/11/20 22:21**/@Testpublic void test01(){//第一步:獲得SqlSession對象SqlSession sqlSession = MybatisUtils.getSqlSession();//方式一:getMapperStudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);List<Student> userList = studentMapper.queryAll();for (Student student : userList) {System.out.println(student);}//關閉SqlSessionsqlSession.close();/*** 輸出如下 :** Student{stuNo=1, stuName='張三', cardID=1115, classID=1}* Student{stuNo=2, stuName='李四', cardID=1116, classID=2}* Student{stuNo=3, stuName='王五', cardID=1117, classID=3}***/}
3.5 模糊查詢
● 1、java代碼執行的時候,傳遞通配符% %
/*** @description: 模糊查詢* @author: zhengyuzhu* @date: 2023/11/21 21:39* @param: like* @return: java.util.List<com.zyz.mybatis.entity.Student>**/
List<Student> getStudentLike(String value);
● 2、在sql拼接中使用通配符!
<!--模糊查詢-->
<select id="getStudentLike" resultType="com.zyz.mybatis.entity.Student">select * from student where stuname like "%"#{value}"%"
</select>
測試單元
/*** @description: 模糊查詢* @author: zhengyuzhu* @date: 2023/11/21 21:41**/
@Test
public void test06(){//第一步:獲得SqlSession對象SqlSession sqlSession = MybatisUtils.getSqlSession();//方式一:getMapperStudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);List<Student> userList = studentMapper.getStudentLike("%李%");for (Student student : userList) {System.out.println(student);}//關閉SqlSessionsqlSession.close();/*** 輸出如下 :** Student{stuNo=2, stuName='李四', cardID=1116, classID=2}* Student{stuNo=8, stuName='李老頭', cardID=8, classID=8}***/}
4、配置解析
可以參考官網的更加詳細的說明,這里對常用的屬性進行說明
MyBatis 的配置文件包含了會深深影響 MyBatis 行為的設置和屬性信息。 配置文檔的頂層結構如下:
● configuration(配置)
○ properties(屬性)
○ settings(設置)
○ typeAliases(類型別名)
○ typeHandlers(類型處理器)
○ objectFactory(對象工廠)
○ plugins(插件)
○ environments(環境配置)
■ environment(環境變量)
● transactionManager(事務管理器)
● dataSource(數據源)
○ databaseIdProvider(數據庫廠商標識)
○ mappers(映射器)
4.1 屬性
我們可以通過properties屬性來實現引用配置文件
這些屬性都是可外部配置且可動態替換的,既可以在典型的Java屬性文件中配置,亦可通過properties元素的子元素來傳遞。【db.properties】
編寫一個配置文件
db.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_test?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8&useSSL=false
jdbc.username=root
jdbc.password=root
在核心配置文件中映入
<!--引入外部配置文件這些屬性可以在外部進行配置,并可以進行動態替換。你既可以在典型的 Java 屬性文件中配置這些屬性,也可以在 properties 元素的子元素中設置。例如:--><properties resource="db.properties"><property name="username" value="root"/><property name="password" value="root"/></properties><dataSource type="POOLED"><!--設置好的屬性可以在整個配置文件中用來替換需要動態配置的屬性值。比如:這個例子中的 username 和 password 將會由 properties 元素中設置的相應值來替換。driver 和 url 屬性將會由 db.properties 文件中對應的值來替換。這樣就為配置提供了諸多靈活選擇。--><property name="driver" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${username}"/><property name="password" value="${password}"/></dataSource>
4.2 設置
設置 參考官網介紹 這里掠過
settings(設置)
4.3 類型別名
● 類型別名是為Java類型設置一個短的名字。
● 存在的意義僅在于用來減少類完全限定名的冗余。
第一種:實體類較少的情況。單獨起別名
<!--可以給實體類起別名-->
<typeAliases><typeAlias type="com.zyz.mybatis.entity.Student" alias="student" />
</typeAliases>
第二種:當實體類很多的時候,可以使用package的形式,會自動的將該包下的所有類定義了別名,別名就是其自身且不區分大小
<typeAliases><package name="com.zyz.mybatis.entity" />
</typeAliases>
第一種可以DIY別名,第二種則不行,如果非要改,需要在實體上增加注解
@Alias("author")
public class Author {...
}
4.4類型處理器(typeHandlers)
MyBatis 在設置預處理語句(PreparedStatement)中的參數或從結果集中取出一個值時, 都會用類型處理器將獲取到的值以合適的方式轉換成 Java 類型。下表描述了一些默認的類型處理器。
類型處理器 參考官網介紹 這里掠過
typeHandlers(類型處理器)
4.5 對象工廠
每次 MyBatis 創建結果對象的新實例時,它都會使用一個對象工廠(ObjectFactory)實例來完成實例化工作。 默認的對象工廠需要做的僅僅是實例化目標類,要么通過默認無參構造方法,要么通過存在的參數映射來調用帶有參數的構造方法。 如果想覆蓋對象工廠的默認行為,可以通過創建自己的對象工廠來實現
對象工廠 參考官網介紹 這里掠過
objectFactory(對象工廠)
4.6 插件(plugins)
具體使用參考官網,略
plugins(插件)
4.7 環境配置
○ environments(環境配置)
■ environment(環境變量)
● transactionManager(事務管理器)
● dataSource(數據源)
MyBatis 可以配置成適應多種環境,這種機制有助于將 SQL 映射應用于多種數據庫之中, 現實情況下有多種理由需要這么做。例如,開發、測試和生產環境需要有不同的配置;或者想在具有相同 Schema 的多個生產數據庫中使用相同的 SQL 映射。還有許多類似的使用場景。
不過要記住:盡管可以配置多個環境,但每個 SqlSessionFactory 實例只能選擇一種環境。
environments 元素定義了如何配置環境。
<environments default="development"><environment id="development"><transactionManager type="JDBC"><property name="..." value="..."/></transactionManager><dataSource type="POOLED"><property name="driver" value="${driver}"/><property name="url" value="${url}"/><property name="username" value="${username}"/><property name="password" value="${password}"/></dataSource></environment>
</environments>
注意一些關鍵點:
● 默認使用的環境 ID(比如:default=“development”)。
● 每個 environment 元素定義的環境 ID(比如:id=“development”)。
● 事務管理器的配置(比如:type=“JDBC”)。
● 數據源的配置(比如:type=“POOLED”)。
默認環境和環境 ID 顧名思義。 環境可以隨意命名,但務必保證默認的環境 ID 要匹配其中一個環境 ID。
4.7.1 environment (環境變量)
注意一些關鍵點:
● 默認使用的環境 ID(比如:default=“development”)。
● 每個 environment 元素定義的環境 ID(比如:id=“development”)。
● 事務管理器的配置(比如:type=“JDBC”)。
● 數據源的配置(比如:type=“POOLED”)。
默認環境和環境 ID 顧名思義。 環境可以隨意命名,但務必保證默認的環境 ID 要匹配其中一個環境 ID。
4.7.2 transactionManager(事務管理器)
如果你正在使用 Spring + MyBatis,則沒有必要配置事務管理器,因為 Spring 模塊會使用自帶的管理器來覆蓋前面的配置。
這兩種事務管理器類型都不需要設置任何屬性。它們其實是類型別名,換句話說,你可以用 TransactionFactory 接口實現類的全限定名或類型別名代替它們。
在 MyBatis 中有兩種類型的事務管理器(也就是 type=“[JDBC|MANAGED]”):
● 1、JDBC – 這個配置直接使用了 JDBC 的提交和回滾功能,它依賴從數據源獲得的連接來管理事務作用域。默認情況下,為了與某些驅動程序兼容,它在關閉連接時啟用自動提交。然而,對于某些驅動程序來說,啟用自動提交不僅是不必要的,而且是一個代價高昂的操作。因此,從 3.5.10 版本開始,你可以通過將 “skipSetAutoCommitOnClose” 屬性設置為 “true” 來跳過這個步驟:例如
● MANAGED – 這個配置幾乎沒做什么。它從不提交或回滾一個連接,而是讓容器來管理事務的整個生命周期(比如 JEE 應用服務器的上下文)。 默認情況下它會關閉連接。然而一些容器并不希望連接被關閉,因此需要將 closeConnection 屬性設置為 false 來阻止默認的關閉行為
4.7.3 dataSource(數據源)
dataSource 元素使用標準的 JDBC 數據源接口來配置 JDBC 連接對象的資源。
大多數 MyBatis 應用程序會按示例中的例子來配置數據源。雖然數據源配置是可選的,但如果要啟用延遲加載特性,就必須配置數據源。
有三種內建的數據源類型(也就是 type=“[UNPOOLED|POOLED|JNDI]”):
● 1、UNPOOLED– 這個數據源的實現會每次請求時打開和關閉連接。雖然有點慢,但對那些數據庫連接可用性要求不高的簡單應用程序來說,是一個很好的選擇。 性能表現則依賴于使用的數據庫,對某些數據庫來說,使用連接池并不重要,這個配置就很適合這種情形。
● 2、POOLED– 這種數據源的實現利用“池”的概念將 JDBC 連接對象組織起來,避免了創建新的連接實例時所必需的初始化和認證時間。 這種處理方式很流行,能使并發 Web 應用快速響應請求。
● 3、JNDI – 這個數據源實現是為了能在如 EJB 或應用服務器這類容器中使用,容器可以集中或在外部配置數據源,然后放置一個 JNDI 上下文的數據源引用
4.8 數據庫廠商標識
具體參考官網,這里掠過
databaseIdProvider(數據庫廠商標識)
4.9 映射器
既然 MyBatis 的行為已經由上述元素配置完了,我們現在就要來定義 SQL 映射語句了。 但首先,我們需要告訴 MyBatis 到哪里去找到這些語句。 在自動查找資源方面,Java 并沒有提供一個很好的解決方案,所以最好的辦法是直接告訴 MyBatis 到哪里去找映射文件。
注意點:
● 接口和它的Mapper配置文件必須同名!
● 接口和它的Mapper配置文件必須在同一個包下!
四種方式
● 1、第一種:使用相對于類路徑的資源引用
<mappers><mapper resource="org/mybatis/builder/AuthorMapper.xml"/><mapper resource="org/mybatis/builder/BlogMapper.xml"/><mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
● 2、第二種:使用完全限定資源定位符(URL)
<mappers><mapper url="file:///var/mappers/AuthorMapper.xml"/><mapper url="file:///var/mappers/BlogMapper.xml"/><mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
● 3、第三種:使用映射器接口實現類的完全限定類名
<mappers><mapper class="org.mybatis.builder.AuthorMapper"/><mapper class="org.mybatis.builder.BlogMapper"/><mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
● 4、第四種:將包內的映射器接口全部注冊為映射器 【使用的最多】
<mappers><package name="org.mybatis.builder"/>
</mappers>
4.10 完整的配置文件(實戰)
4.10.1 db.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_test?serverTimezone=UTC&useUnicode=true&characterEncoding=UTF-8&useSSL=false
jdbc.username=root
jdbc.password=root
4.10.2 mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--configuration core file--><configuration><!--引入外部配置文件這些屬性可以在外部進行配置,并可以進行動態替換。你既可以在典型的 Java 屬性文件中配置這些屬性,也可以在 properties 元素的子元素中設置。例如:--><properties resource="db.properties"><property name="username" value="root"/><property name="password" value="root"/></properties><!--1、可以給實體類起別名<typeAliases><typeAlias type="com.zyz.mybatis.entity.Student" alias="student" /></typeAliases>2、 批量設置別名,會自動的將該包下的所有類定義了別名,別名就是其自身且不區分大小--><typeAliases><package name="com.zyz.mybatis.entity" /></typeAliases><!--MyBatis 可以配置成適應多種環境,這種機制有助于將 SQL 映射應用于多種數據庫之中, 現實情況下有多種理由需要這么做。例如,開發、測試和生產環境需要有不同的配置;或者想在具有相同 Schema 的多個生產數據庫中使用相同的 SQL 映射。還有許多類似的使用場景。不過要記住:盡管可以配置多個環境,但每個 SqlSessionFactory 實例只能選擇一種環境注意一些關鍵點:默認使用的環境 ID(比如:default="development")。每個 environment 元素定義的環境 ID(比如:id="development")。事務管理器的配置(比如:type="JDBC")。數據源的配置(比如:type="POOLED")。默認環境和環境 ID 顧名思義。 環境可以隨意命名,但務必保證默認的環境 ID 要匹配其中一個環境 ID。--><environments default="development"><environment id="development"><!--JDBC – 這個配置直接使用了 JDBC 的提交和回滾功能,它依賴從數據源獲得的連接來管理事務作用域。默認情況下,為了與某些驅動程序兼容,它在關閉連接時啟用自動提交。--><transactionManager type="JDBC"/><!--dataSource 元素使用標準的 JDBC 數據源接口來配置 JDBC 連接對象的資源大多數 MyBatis 應用程序會按示例中的例子來配置數據源。雖然數據源配置是可選的,但如果要啟用延遲加載特性,就必須配置數據源。POOLED– 這種數據源的實現利用“池”的概念將 JDBC 連接對象組織起來,避免了創建新的連接實例時所必需的初始化和認證時間。這種處理方式很流行,能使并發 Web 應用快速響應請求。--><dataSource type="POOLED"><!--設置好的屬性可以在整個配置文件中用來替換需要動態配置的屬性值。比如:這個例子中的 username 和 password 將會由 properties 元素中設置的相應值來替換。driver 和 url 屬性將會由 db.properties 文件中對應的值來替換。這樣就為配置提供了諸多靈活選擇。--><property name="driver" value="${jdbc.driver}"/><property name="url" value="${jdbc.url}"/><property name="username" value="${username}"/><property name="password" value="${password}"/></dataSource></environment></environments><!--加載映射配置文件既然 MyBatis 的行為已經由上述元素配置完了,我們現在就要來定義 SQL 映射語句了。但首先,我們需要告訴 MyBatis 到哪里去找到這些語句。 在自動查找資源方面,Java 并沒有提供一個很好的解決方案,所以最好的辦法是直接告訴 MyBatis 到哪里去找映射文件。你可以使用相對于1、類路徑的資源引用2、完全限定資源定位符(包括 file:/// 形式的 URL)3、類名4、包名:--><!-- 1、使用相對于類路徑的資源引用 --><mappers><mapper resource="com/zyz/mybatis/mapper/StudentMapper.xml"/></mappers><!--2、使用完全限定資源定位符(URL)<mappers><mapper url="file:///var/mappers/AuthorMapper.xml"/><mapper url="file:///var/mappers/BlogMapper.xml"/><mapper url="file:///var/mappers/PostMapper.xml"/></mappers>--><!--3、使用映射器接口實現類的完全限定類名<mappers><mapper class="org.mybatis.builder.AuthorMapper"/><mapper class="org.mybatis.builder.BlogMapper"/><mapper class="org.mybatis.builder.PostMapper"/></mappers>--><!--4、將包內的映射器接口全部注冊為映射器<mappers><package name="org.mybatis.builder"/></mappers>-->
</configuration>
4.10.3 項目結構
5、解決屬性名和字段名不一致的問題
數據庫 和 實體類對應關系如下:
5.1 通過字段別名解決
2、通過給字段起別名解決
<select id="queryGoodsById" resultType="com.zyz.mybatis.entity.Goods">select id,name as goods_Name,amount as goods_Amount,price as goods_Pricefrom goodswhere id = #{id}
</select>
5.2 通過結果集映射(resultMap)
3、通過結果集映射
<select id="queryGoodsById" resultMap="GoodsMap">select *from goodswhere id = #{id}
</select><!-- 結果集映射 -->
<resultMap id="GoodsMap" type="Goods"><!--column數據庫中的字段,property實體類中的屬性--><result column="id" property="id" /><result column="name" property="goods_Name" /><result column="amount" property="goods_Amount" /><result column="price" property="goods_Price" />
</resultMap>
測試單元
/*** @description: 測試查詢商品信息 數據庫字段 和 實體類字段不一致問題* @author: zhengyuzhu* @date: 2023/11/22 9:19**/@Testpublic void testDemo1(){//第一步:獲得SqlSession對象SqlSession sqlSession = MybatisUtils.getSqlSession();//方式一:getMapperGoodsMapper goodsMapper = sqlSession.getMapper(GoodsMapper.class);Goods goods = goodsMapper.queryGoodsById(1001);System.out.println(goods);//關閉SqlSessionsqlSession.close();/*** 輸出如下 :* 實體類字段 數據庫字段* id id* goods_Name name* goods_Amount amount* goods_Price price** Goods{id=1001, goods_Name='null', goods_Amount=null, goods_Price=null}**** 第一種方式:字段起別名:* select id,name as goods_Name,amount as goods_Amount,price as goods_Price** Goods{id=1001, goods_Name='茶杯', goods_Amount=10, goods_Price=13.6}*** 第二種方式:結果集映射:* 具體實現查看 GoodsMapper.xml 文件注釋描述** Goods{id=1001, goods_Name='茶杯', goods_Amount=10, goods_Price=13.6}***/}
6、日志
Mybatis 通過使用內置的日志工廠提供日志功能。內置日志工廠將會把日志工作委托給下面的實現之一:
● SLF4J
● Apache Commons Logging
● Log4j 2
● Log4j(3.5.9起廢棄)
● JDK logging
MyBatis 內置日志工廠基于運行時自省機制選擇合適的日志工具。
6.1 Log4j 的使用
什么是Log4j?
● Log4j是Apache的一個開源項目,通過使用Log4j,我們可以控制日志信息輸送的目的地是控制臺、文件、GUI組件
● 我們也可以控制每一條日志的輸出格式;
● 通過定義每一條日志信息的級別,我們能夠更加細致地控制日志的生成過程。
● 可以通過一個配置文件來靈活地進行配置,而不需要修改應用的代碼。
具體使用過程如下:
1、先在pom.xml文件中導入log4j的依賴包
<!-- Log4j 配置 -->
<dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version>
</dependency>
2、在resources文件夾下建立log4j.properties文件進行配置
這里將 文件輸出的位置 放在根目錄 ./log/zyz.log
#將等級為DEBUG的日志信息輸出到console和file這兩個目的地,console和file的定義在下面的代碼
log4j.rootLogger = DEBUG,console ,file#控制臺輸出的相關設置
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold = DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern = [%c]-%m%n#文件輸出的相關設置
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File = ./log/zyz.log
log4j.appender.file.MaxFileSize = 10mb
log4j.appender.file.Threshold = DEBUG
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern = [%p][%d{yy-MM-dd}][%c]%m%n#日志輸出級別
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
3、在mybatis-config.xml核心配置文件中,配置log4j為日志的實現!
<settings><setting name="logImpl" value="LOG4J"/>
</settings>
4、Log4j的使用,直接測試運行
在被測試的類中添加 這段信息
static Logger logger = Logger.getLogger(TestGoods.class);
import com.zyz.mybatis.entity.Goods;
import com.zyz.mybatis.mapper.GoodsMapper;
import com.zyz.mybatis.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
import org.junit.Test;/*** @BelongsProject: Mybatis* @BelongsPackage: PACKAGE_NAME* @Author: zhengyuzhu* @CreateTime: 2023-11-22 09:19* @Description: TODO* @Version: 1.0*/
public class TestGoods {static Logger logger = Logger.getLogger(TestGoods.class);/*** @description: 測試日志打印* @author: zhengyuzhu* @date: 2023/11/22 10:03**/@Testpublic void testDemo2(){logger.info("INFO 日志測試");logger.debug("DEBUG 日志測試");logger.error("ERROR 日志測試");}
}
輸出結果入下:
7、分頁
思考:為什么要分頁!
● 減少數據的處理量
7.1 使用Limit分頁
語法:SELECT * from user limit startIndex,pageSize
SELECT * from user limit 3 #[0,n]
1、接口
/*** @description: 使用 limit 分頁查詢數據* @author: zhengyuzhu* @date: 2023/11/22 10:56* @param: map* @return: java.util.List<com.zyz.mybatis.entity.Goods>**/
List<Goods> queryGoodsLimit(Map<String,Object> map);
2、GoodsMapper.xml 文件
<!--分頁查詢數據-->
<select id="queryGoodsLimit" resultMap="GoodsMap">select *from goods limit #{startIndex},#{pageSize}
</select><!-- 結果集映射 -->
<resultMap id="GoodsMap" type="Goods"><!--column數據庫中的字段,property實體類中的屬性--><result column="id" property="id" /><result column="name" property="goods_Name" /><result column="amount" property="goods_Amount" /><result column="price" property="goods_Price" />
</resultMap>
3、測試單元
/*** @description: limit 分頁查詢數據* @author: zhengyuzhu* @date: 2023/11/22 10:59**/
@Test
public void testDemo3(){//第一步:獲得SqlSession對象SqlSession sqlSession = MybatisUtils.getSqlSession();//方式一:getMapperGoodsMapper goodsMapper = sqlSession.getMapper(GoodsMapper.class);HashMap<String, Object> goodsMap = new HashMap<String, Object>();goodsMap.put("startIndex",0);goodsMap.put("pageSize",3);List<Goods> goodsList = goodsMapper.queryGoodsLimit(goodsMap);for(Goods goods : goodsList){logger.info(goods);}//關閉SqlSessionsqlSession.close();/*** 輸出如下 :** [com.zyz.mybatis.mapper.GoodsMapper.queryGoodsLimit]-==> Preparing: select * from goods limit ?,?* [com.zyz.mybatis.mapper.GoodsMapper.queryGoodsLimit]-==> Parameters: 0(Integer), 3(Integer)* [com.zyz.mybatis.mapper.GoodsMapper.queryGoodsLimit]-<== Total: 3* [TestGoods]-Goods{id=1001, goods_Name='茶杯', goods_Amount=10, goods_Price=13.6}* [TestGoods]-Goods{id=1002, goods_Name='茶壺', goods_Amount=10, goods_Price=15.9}* [TestGoods]-Goods{id=1003, goods_Name='保溫杯', goods_Amount=10, goods_Price=23.6}***/
}
7.2 RowBounds分頁
1.接口
/*** @description: 使用 RowBounds 分頁* @author: zhengyuzhu* @date: 2023/11/22 11:12* @return: java.util.List<com.zyz.mybatis.entity.Goods>**/
List<Goods> queryGoodsByRowBounds();
2 .GoodsMapper.xml 文件
<!--使用 RowBounds 分頁-->
<select id="queryGoodsByRowBounds" resultMap="GoodsMap">select * from goods
</select><!-- 結果集映射 -->
<resultMap id="GoodsMap" type="Goods"><!--column數據庫中的字段,property實體類中的屬性--><result column="id" property="id" /><result column="name" property="goods_Name" /><result column="amount" property="goods_Amount" /><result column="price" property="goods_Price" />
</resultMap>
3.測試單元
/*** @description: 2、使用 RowBounds 分頁* @author: zhengyuzhu* @date: 2023/11/22 11:14**/
@Test
public void testDemo4(){//第一步:獲得SqlSession對象SqlSession sqlSession = MybatisUtils.getSqlSession();// RowBounds 實現RowBounds rowBounds = new RowBounds(0, 2);List<Goods> goodsList = sqlSession.selectList("com.zyz.mybatis.mapper.GoodsMapper.queryGoodsByRowBounds",null,rowBounds);for(Goods goods : goodsList){logger.info(goods);}//關閉SqlSessionsqlSession.close();/*** 輸出如下 :** [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@77a57272]* [com.zyz.mybatis.mapper.GoodsMapper.queryGoodsByRowBounds]-==> Preparing: select * from goods* [com.zyz.mybatis.mapper.GoodsMapper.queryGoodsByRowBounds]-==> Parameters:* [TestGoods]-Goods{id=1001, goods_Name='茶杯', goods_Amount=10, goods_Price=13.6}* [TestGoods]-Goods{id=1002, goods_Name='茶壺', goods_Amount=10, goods_Price=15.9}***/
}
7.3 分頁插件
官網介紹:鏈接https://pagehelper.github.io/
使用分頁插件的流程:
● 1、在maven中引入PageHelper和jsqlparser
● 2、mybatis-config.xml中增加plugin配置
● 3、代碼中使用PageHelper.startPage() 自動分頁
1、首先,在maven中引入PageHelper和jsqlparser
<!--分頁插件-->
<dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper</artifactId><version>5.1.4</version>
</dependency>
<dependency><groupId>com.github.jsqlparser</groupId><artifactId>jsqlparser</artifactId><version>1.4</version>
</dependency>
說明:
(1)jsqlparser的版本需要是2.0及以上的版本;因為5.1.10之前的PageHelper并沒有對jsqlparser版本作要求;但是5.1.10及以后的PageHelper采用了新的API,所以jsqlparser的版本需要是2.0及以上的版本才可以;【我這里退回 1.4 可以正常使用】
(2)PageHelper執行原理:PageHelper在原有的要執行的SQL基礎上,進行分析,自動生成分頁以及【select count(*) from…】這樣的語句;因為,涉及到原始SQL的分析與解析,所以需要引入jsqlparser這個SQL解析器組件啦;
2、mybatis-config.xml中增加plugin配置
參數詳解:Mybatis-PageHelper
官網:官網文檔
plugins插件是mybatis的一個特色,通過插件可以擴展mybatis的能力;
(2)interceptor屬性:代表攔截器,需要配置PageHelper的核心類;
(3)helperDialect配置項和reasonable配置項(在PageHelper的官網文檔中有詳細說明)
<plugins><!-- com.github.pagehelper為PageHelper類所在包名 --><plugin interceptor="com.github.pagehelper.PageInterceptor"><!--使用下面的方式配置參數helperDialect:分頁插件會自動檢測當前的數據庫鏈接,自動選擇合適的分頁方式。你可以配置helperDialect屬性來指定分頁插件使用哪種方言 配置時,可以使用下面的縮寫值:oracle,mysql,mariadb,sqlite,hsqldb,postgresql,db2,sqlserver,informix,h2,sqlserver2012,derbyreasonable:分頁合理化參數,默認值為false。當該參數設置為 true 時,pageNum<=0 時會查詢第一頁, pageNum>pages(超過總數時),會查詢最后一頁。默認false 時,直接根據參數進行查詢。更多參數配置:參考官網--><property name="helperDialect" value="mysql"/><property name="reasonable" value="true"/></plugin>
</plugins>
3、測試單元
/*** @description: 3、使用分頁插件* @author: zhengyuzhu* @date: 2023/11/22 13:43**/@Testpublic void testDemo5(){//第一步:獲得SqlSession對象SqlSession sqlSession = MybatisUtils.getSqlSession();// PageHelper.startPage()對即將到來的下一次查詢進行分頁處理;// 比如下面就是第1頁,每頁2條數據;PageHelper.startPage(1, 2);GoodsMapper goodsMapper = sqlSession.getMapper(GoodsMapper.class);List<Goods> goodsList = goodsMapper.queryGoodByPlugin();//用PageInfo對結果進行包裝PageInfo page = new PageInfo(goodsList);System.out.println("總頁數:"+page.getPages());System.out.println("總記錄數:"+page.getTotal());System.out.println("開始行號:"+page.getStartRow());System.out.println("結束行號:"+page.getEndRow());System.out.println("當前頁碼:"+page.getPageNum());List<Goods> goodsListRs = page.getList();for(Goods goods : goodsListRs){logger.info(goods);}//關閉SqlSessionsqlSession.close();/*** 輸出如下 :*[com.zyz.mybatis.mapper.GoodsMapper.queryGoodByPlugin_COUNT]-==> Preparing: SELECT count(0) FROM goods[com.zyz.mybatis.mapper.GoodsMapper.queryGoodByPlugin_COUNT]-==> Parameters: [com.zyz.mybatis.mapper.GoodsMapper.queryGoodByPlugin_COUNT]-<== Total: 1[com.zyz.mybatis.mapper.GoodsMapper.queryGoodByPlugin]-==> Preparing: select * from goods LIMIT ?[com.zyz.mybatis.mapper.GoodsMapper.queryGoodByPlugin]-==> Parameters: 2(Integer)[com.zyz.mybatis.mapper.GoodsMapper.queryGoodByPlugin]-<== Total: 2總頁數:2總記錄數:4開始行號:1結束行號:2當前頁碼:1[TestGoods]-Goods{id=1001, goods_Name='茶杯', goods_Amount=10, goods_Price=13.6}[TestGoods]-Goods{id=1002, goods_Name='茶壺', goods_Amount=10, goods_Price=15.9}**/}
8、使用注解開發
8.1 面向接口編程
● 之前學過面向對象編程,也學習過接口,但在真正的開發中,很多時候會選擇面向接口編程。
● 根本原因:解耦,可拓展,提高復用,分層開發中,上層不用管具體的實現,大家都遵守共同的標準,使得開發變得容易,規范性更好
● 在一個面向對象的系統中,系統的各種功能是由許許多多的不同對象協作完成的。在這種情況下,各個對象內部是如何實現自己的,對系統設計人員來講就不那么重要了;
● 而各個對象之間的協作關系則成為系統設計的關鍵。小到不同類之間的通信,大到各模塊之間的交互,在系統設計之初都是要著重考慮的,這也是系統設計的主要工作內容。面向接口編程就是指按照這種思想來編程。
8.2 注解開發
8.2.1 基本測試
- 注解在StudentMapper接口上實現,不需要 StudentMapper.xml文件
/*** @description: 查詢用戶* @author: zhengyuzhu* @date: 2023/11/22 14:40* @return: java.util.List<com.zyz.mybatis.entity.User>**/@Select("select * from user")List<User> getUsers();
- 需要在mybatis-config.xml核心配置文件中綁定接口
<mappers><mapper resource="com/zyz/mybatis/mapper/StudentMapper.xml"/><mapper resource="com/zyz/mybatis/mapper/GoodsMapper.xml"/><mapper class="com.zyz.mybatis.mapper.UserMapper"/>
</mappers>
- 單元測試
/*** @description: 注解開發 查詢* @author: zhengyuzhu* @date: 2023/11/22 14:41**/@Testpublic void testDemo1(){//第一步:獲得SqlSession對象SqlSession sqlSession = MybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);List<User> users = mapper.getUsers();for (User user : users) {System.out.println(user);}sqlSession.close();/*** 輸出如下:** [com.zyz.mybatis.mapper.UserMapper.getUsers]-==> Preparing: select * from user* [com.zyz.mybatis.mapper.UserMapper.getUsers]-==> Parameters:* [com.zyz.mybatis.mapper.UserMapper.getUsers]-<== Total: 6* User{id=1, name='張三', password='null'}* User{id=2, name='李四', password='null'}* User{id=3, name='王五', password='null'}* User{id=4, name='麻子', password='null'}* User{id=5, name='李白', password='null'}* User{id=6, name='小明', password='null'}****/}
8.2.2 測試增刪改查
當數據表字段名和POJO字段名不一致時,需要進行映射:
/*** @description: 查詢用戶* @author: zhengyuzhu* @date: 2023/11/22 14:40* @return: java.util.List<com.zyz.mybatis.entity.User>**/
@Select("select * from user")
@Results(value = {@Result(id=true,column = "id",property = "id"),@Result(column = "name",property = "name"),@Result(column = "pwd",property = "password")
})
List<User> getUsers();
測試單元
/*** @description: 注解開發 查詢* @author: zhengyuzhu* @date: 2023/11/22 14:41**/@Testpublic void testDemo1(){//第一步:獲得SqlSession對象SqlSession sqlSession = MybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);List<User> users = mapper.getUsers();for (User user : users) {System.out.println(user);}sqlSession.close();/*** 輸出如下:** [com.zyz.mybatis.mapper.UserMapper.getUsers]-==> Preparing: select * from user* [com.zyz.mybatis.mapper.UserMapper.getUsers]-==> Parameters:* [com.zyz.mybatis.mapper.UserMapper.getUsers]-<== Total: 6* User{id=1, name='張三', password='null'}* User{id=2, name='李四', password='null'}* User{id=3, name='王五', password='null'}* User{id=4, name='麻子', password='null'}* User{id=5, name='李白', password='null'}* User{id=6, name='小明', password='null'}* * * 采用結果集映射后* * [com.zyz.mybatis.mapper.UserMapper.getUsers]-==> Preparing: select * from user* [com.zyz.mybatis.mapper.UserMapper.getUsers]-==> Parameters: * [com.zyz.mybatis.mapper.UserMapper.getUsers]-<== Total: 7* User{id=1, name='張三', password='123'}* User{id=2, name='李四', password='456'}* User{id=3, name='王五', password='789'}* User{id=4, name='麻子', password='321'}* User{id=5, name='李白', password='654'}* User{id=6, name='小明', password='987'}* User{id=7, name='小紅', password='666'}****/}
其他接口,如何重復使用上面的那個映射呢?如下:
測試單元
/*** @description: 查詢 通過ID* @author: zhengyuzhu* @date: 2023/11/22 15:15**/@Testpublic void testDemo2(){//第一步:獲得SqlSession對象SqlSession sqlSession = MybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);User user = mapper.getUserById(1);System.out.println(user);sqlSession.close();/*** 輸出如下:** [com.zyz.mybatis.mapper.UserMapper.getUserById]-==> Preparing: select * from user where id=?* [com.zyz.mybatis.mapper.UserMapper.getUserById]-==> Parameters: 1(Integer)* [com.zyz.mybatis.mapper.UserMapper.getUserById]-<== Total: 1* User{id=1, name='張三', password='null'}* * 復用結果集映射* [com.zyz.mybatis.mapper.UserMapper.getUserById]-==> Preparing: select * from user where id=?* [com.zyz.mybatis.mapper.UserMapper.getUserById]-==> Parameters: 1(Integer)* [com.zyz.mybatis.mapper.UserMapper.getUserById]-<== Total: 1* User{id=1, name='張三', password='123'}***/}
8.2.2 查詢
如果實體類字段 和 數據庫字段不一致 需要進行接口 結果集映射
@Results(id = "UserMap",value = {@Result(id=true,column = "id",property = "id"),@Result(column = "name",property = "name"),@Result(column = "pwd",property = "password")})
這里添加一個 id 可以達到復用的效果。
其它接口使用結果集的時候,可以直接用: @ResultMap(value = (“UserMap”))
接口
/*** @description: 查詢用戶* @author: zhengyuzhu* @date: 2023/11/22 14:40* @return: java.util.List<com.zyz.mybatis.entity.User>**/@Select("select * from user")@Results(id = "UserMap",value = {@Result(id=true,column = "id",property = "id"),@Result(column = "name",property = "name"),@Result(column = "pwd",property = "password")})List<User> getUsers();/*** @description: 查詢用戶 通過 用戶ID 方法存在多個參數,所有參數前面必須加上@Param("id")注解* @author: zhengyuzhu* @date: 2023/11/22 15:12* @param: id* @return: com.zyz.mybatis.entity.User**/@Select("select * from user where id=#{id}")@ResultMap(value = ("UserMap"))User getUserById(@Param("id") int id);
測試單元
/*** @description: 注解開發 查詢* @author: zhengyuzhu* @date: 2023/11/22 14:41**/
@Test
public void testDemo1(){//第一步:獲得SqlSession對象SqlSession sqlSession = MybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);List<User> users = mapper.getUsers();for (User user : users) {System.out.println(user);}sqlSession.close();/*** 輸出如下:** [com.zyz.mybatis.mapper.UserMapper.getUsers]-==> Preparing: select * from user* [com.zyz.mybatis.mapper.UserMapper.getUsers]-==> Parameters:* [com.zyz.mybatis.mapper.UserMapper.getUsers]-<== Total: 6* User{id=1, name='張三', password='null'}* User{id=2, name='李四', password='null'}* User{id=3, name='王五', password='null'}* User{id=4, name='麻子', password='null'}* User{id=5, name='李白', password='null'}* User{id=6, name='小明', password='null'}*** 采用結果集映射后** [com.zyz.mybatis.mapper.UserMapper.getUsers]-==> Preparing: select * from user* [com.zyz.mybatis.mapper.UserMapper.getUsers]-==> Parameters:* [com.zyz.mybatis.mapper.UserMapper.getUsers]-<== Total: 7* User{id=1, name='張三', password='123'}* User{id=2, name='李四', password='456'}* User{id=3, name='王五', password='789'}* User{id=4, name='麻子', password='321'}* User{id=5, name='李白', password='654'}* User{id=6, name='小明', password='987'}* User{id=7, name='小紅', password='666'}****/
}/*** @description: 查詢 通過ID* @author: zhengyuzhu* @date: 2023/11/22 15:15**/
@Test
public void testDemo2(){//第一步:獲得SqlSession對象SqlSession sqlSession = MybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);User user = mapper.getUserById(1);//查詢System.out.println(user);sqlSession.close();/*** 輸出如下:** [com.zyz.mybatis.mapper.UserMapper.getUserById]-==> Preparing: select * from user where id=?* [com.zyz.mybatis.mapper.UserMapper.getUserById]-==> Parameters: 1(Integer)* [com.zyz.mybatis.mapper.UserMapper.getUserById]-<== Total: 1* User{id=1, name='張三', password='null'}** 復用結果集映射* [com.zyz.mybatis.mapper.UserMapper.getUserById]-==> Preparing: select * from user where id=?* [com.zyz.mybatis.mapper.UserMapper.getUserById]-==> Parameters: 1(Integer)* [com.zyz.mybatis.mapper.UserMapper.getUserById]-<== Total: 1* User{id=1, name='張三', password='123'}***/
}
8.2.3 添加
接口
/*** @description: 添加用戶* @author: zhengyuzhu* @date: 2023/11/22 15:11* @param: user* @return: int**/
@Insert("insert into user (id,name,pwd) values(#{id},#{name},#{password})")
int addUser(User user);
測試單元
/*** @description: 添加用戶* @author: zhengyuzhu* @date: 2023/11/22 15:15**/@Testpublic void testDemo3(){//第一步:獲得SqlSession對象SqlSession sqlSession = MybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);User user = new User();user.setId(8);user.setName("小青");user.setPassword("888");mapper.addUser(user);//添加User userRs = mapper.getUserById(8);System.out.println("新添加用戶 : " + userRs);sqlSession.close();/*** 輸出如下:** [com.zyz.mybatis.mapper.UserMapper.addUser]-==> Preparing: insert into user (id,name,pwd) values(?,?,?)* [com.zyz.mybatis.mapper.UserMapper.addUser]-==> Parameters: 8(Integer), 小青(String), 888(String)* [com.zyz.mybatis.mapper.UserMapper.addUser]-<== Updates: 1* [com.zyz.mybatis.mapper.UserMapper.getUserById]-==> Preparing: select * from user where id=?* [com.zyz.mybatis.mapper.UserMapper.getUserById]-==> Parameters: 8(Integer)* [com.zyz.mybatis.mapper.UserMapper.getUserById]-<== Total: 1* 新添加用戶 : User{id=8, name='小青', password='888'}***/}
8.2.4 修改
接口
/*** @description: 修改用戶* @author: zhengyuzhu* @date: 2023/11/22 15:11* @param: user* @return: int**/
@Update("update user set name=#{name},pwd=#{password} where id=#{id}")
int updateUser(User user);
測試單元
/*** @description: 修改用戶* @author: zhengyuzhu* @date: 2023/11/22 15:15**/@Testpublic void testDemo4(){//第一步:獲得SqlSession對象SqlSession sqlSession = MybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);User userRs = mapper.getUserById(8);System.out.println("未修改前用戶信息 : " + userRs);User user = new User();user.setId(8);user.setName("小紫");user.setPassword("999");mapper.updateUser(user);//修改User userRss = mapper.getUserById(8);System.out.println("修改后用戶信息 : " + userRss);sqlSession.close();/*** 輸出如下:** [com.zyz.mybatis.mapper.UserMapper.getUserById]-==> Preparing: select * from user where id=?* [com.zyz.mybatis.mapper.UserMapper.getUserById]-==> Parameters: 8(Integer)* [com.zyz.mybatis.mapper.UserMapper.getUserById]-<== Total: 1* 未修改前用戶信息 : User{id=8, name='小青', password='888'}* [com.zyz.mybatis.mapper.UserMapper.updateUser]-==> Preparing: update user set name=?,pwd=? where id=?* [com.zyz.mybatis.mapper.UserMapper.updateUser]-==> Parameters: 小紫(String), 999(String), 8(Integer)* [com.zyz.mybatis.mapper.UserMapper.updateUser]-<== Updates: 1* [com.zyz.mybatis.mapper.UserMapper.getUserById]-==> Preparing: select * from user where id=?* [com.zyz.mybatis.mapper.UserMapper.getUserById]-==> Parameters: 8(Integer)* [com.zyz.mybatis.mapper.UserMapper.getUserById]-<== Total: 1* 修改后用戶信息 : User{id=8, name='小紫', password='999'}***/}
8.2.5 刪除
接口
/*** @description:刪除用戶 可以通過@Param("") 給傳遞的參數 修改別名* @author: zhengyuzhu* @date: 2023/11/22 15:11* @param: id* @return: int**/
@Delete("delete from user where id = #{uid}")
int deleteUser(@Param("uid") int id);
測試單元
/*** @description: 刪除用戶* @author: zhengyuzhu* @date: 2023/11/22 15:15**/@Testpublic void testDemo5(){//第一步:獲得SqlSession對象SqlSession sqlSession = MybatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);User userRs = mapper.getUserById(8);System.out.println("刪除前用戶信息 : " + userRs);mapper.deleteUser(8);//刪除User userRss = mapper.getUserById(8);System.out.println("刪除后用戶信息 : " + userRss);sqlSession.close();/*** 輸出如下:** [com.zyz.mybatis.mapper.UserMapper.getUserById]-==> Preparing: select * from user where id=?* [com.zyz.mybatis.mapper.UserMapper.getUserById]-==> Parameters: 8(Integer)* [com.zyz.mybatis.mapper.UserMapper.getUserById]-<== Total: 1* 刪除前用戶信息 : User{id=8, name='小紫', password='999'}* [com.zyz.mybatis.mapper.UserMapper.deleteUser]-==> Preparing: delete from user where id = ?* [com.zyz.mybatis.mapper.UserMapper.deleteUser]-==> Parameters: 8(Integer)* [com.zyz.mybatis.mapper.UserMapper.deleteUser]-<== Updates: 1* [com.zyz.mybatis.mapper.UserMapper.getUserById]-==> Preparing: select * from user where id=?* [com.zyz.mybatis.mapper.UserMapper.getUserById]-==> Parameters: 8(Integer)* [com.zyz.mybatis.mapper.UserMapper.getUserById]-<== Total: 0* 刪除后用戶信息 : null***/}
9、Lombok(偷懶的話可以使用)
使用步驟:
- 在IDEA中安裝Lombok插件!
- 在項目pom.xml文件中導入Lombok的jar包
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.10</version></dependency>
- 在實體類上加注解即可!
@Data
@AllArgsConstructor
@NoArgsConstructor@Getter and @Setter
@FieldNameConstants
@ToString
@EqualsAndHashCode
@AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor
@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
@Data
@Builder
@SuperBuilder
@Singular
@Delegate
@Value
@Accessors
@Wither
@With
@SneakyThrows
說明:
@Data:無參構造、get、set、toString、hashCode、equals
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
@ToString
@Getter and @Setter
10、多對一處理
多對一:
● 多個學生,對應一個老師
● 對于學生而言,關聯–多個學生,關聯一個老師【多對一】
● 對于老師而言,集合–一個老師,有很多個學生【一對多】
SQL 語句
CREATE TABLE `tb_teacher` (`id` INT(10) NOT NULL,`name` VARCHAR(30) DEFAULT NULL,PRIMARY KEY (`id`)
)ENGINE = INNODB DEFAULT CHARSET=utf8INSERT INTO tb_teacher(`id`,`name`) VALUES (1,'玉小剛');CREATE TABLE `tb_student` (`id` INT(10) NOT NULL,`name` VARCHAR(30) DEFAULT NULL,`tid` INT(10) DEFAULT NULL,PRIMARY KEY (`id`),KEY `fktid`(`tid`),CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
)ENGINE = INNODB DEFAULT CHARSET=utf8INSERT INTO `tb_student`(`id`,`name`,`tid`) VALUES ('1','唐三','1');
INSERT INTO `tb_student`(`id`,`name`,`tid`) VALUES ('2','小舞','1');
INSERT INTO `tb_student`(`id`,`name`,`tid`) VALUES ('3','戴沐白','1');
INSERT INTO `tb_student`(`id`,`name`,`tid`) VALUES ('4','朱朱清','1');
INSERT INTO `tb_student`(`id`,`name`,`tid`) VALUES ('5','奧斯卡','1');
INSERT INTO `tb_student`(`id`,`name`,`tid`) VALUES ('6','寧榮榮','1');
INSERT INTO `tb_student`(`id`,`name`,`tid`) VALUES ('7','馬紅俊','1');
INSERT INTO `tb_student`(`id`,`name`,`tid`) VALUES ('8','白塵香','1');
測試環境搭建
- 導入Lombok
- 新建實體類Teacher,Student
- mybatis核心配置文件修改
- 建立Mapper接口
- 建立Mapper.XML文件
- 在核心配置文件中綁定注冊我們的Mapper接口或者文件!【方式很多,隨心選】
- 測試查詢是否能夠成功!
1、導入Lombok
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.10</version></dependency>
2、實體類Teacher,Student
@Data //get,set
@NoArgsConstructor //無參構造
@AllArgsConstructor //有參構造
public class Student {private Integer id;private String name;private Teacher teacher;}@Data //get,set
@NoArgsConstructor //無參構造
@AllArgsConstructor //有參構造
public class Teacher {private Integer id;private String name;}
3、mybatis 配置文件
<typeAliases><package name="com.zyz.mybatis.entity" /><package name="com.zyz.mybatis.vo" />
</typeAliases><mappers><mapper resource="com/zyz/mybatis/mapper/StudentMapper.xml"/><mapper resource="com/zyz/mybatis/mapper/TeacherMapper.xml"/>
</mappers>
4、mapper 接口
public interface StudentMapper {/*** @description: 查詢學生* @author: zhengyuzhu* @date: 2023/11/22 22:27* @return: java.util.List<com.zyz.mybatis.entity.Student>**/List<Student> getStudent();/*** @description: 查詢學生* @author: zhengyuzhu* @date: 2023/11/22 22:27* @return: java.util.List<com.zyz.mybatis.entity.Student>**/List<Student> getStudent2();}
5、mapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zyz.mybatis.mapper.StudentMapper"><!--1、按照結果集嵌套處理這里重點說一下 這個結果集映射。如果對查詢出來的數據字段 起了 別名。則映射的時候 要用這個別名。--><resultMap id="StudentMap" type="Student"><!--這里的column是與查詢結果的字段名對應,字段重命名了則對應命名后的字段--><result property="id" column="sid"/><result property="name" column="sname"/><!--復雜的屬性需要單獨處理 對象:association 集合:collection--><association property="teacher" javaType="Teacher"><result property="name" column="tname"/><result property="id" column="tid"/></association></resultMap><!--查詢學生--><select id="getStudent" resultMap="StudentMap">select s.id sid,s.name sname,t.name tname,s.tid tidfrom tb_student sjoin tb_teacher ton s.tid = t.id</select><!--2、 按照查詢嵌套處理--><select id="getStudent2" resultMap="StudentMap2">select * from tb_student;</select><select id="getTeacher" resultType="Teacher">select * from tb_teacher where id = #{tid};</select><!--這里將學生和對應老師分開查詢,將查詢結果組合--><resultMap id="StudentMap2" type="Student"><result property="id" column="id"/><result property="name" column="name"/><association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/></resultMap>
</mapper>
6、測試單元
/*** @description: 1、按照結果集嵌套處理 多對一* @author: zhengyuzhu* @date: 2023/11/22 23:07**/
@Test
public void testDemo1(){//第一步:獲得SqlSession對象SqlSession sqlSession = MybatisUtils.getSqlSession();StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);List<Student> studentList = studentMapper.getStudent();for(Student student : studentList){System.out.println(student);}sqlSession.close();/*** 輸出如下:** [com.zyz.mybatis.mapper.StudentMapper.getStudent]-==> Preparing: select s.id sid,s.name sname,t.name tname,s.tid tid from tb_student s join tb_teacher t on s.tid = t.id* [com.zyz.mybatis.mapper.StudentMapper.getStudent]-==> Parameters:* [com.zyz.mybatis.mapper.StudentMapper.getStudent]-<== Total: 8* Student{id=1, name='唐三', teacher=Teacher{id=1, name='玉小剛'}}* Student{id=2, name='小舞', teacher=Teacher{id=1, name='玉小剛'}}* Student{id=3, name='戴沐白', teacher=Teacher{id=1, name='玉小剛'}}* Student{id=4, name='朱朱清', teacher=Teacher{id=1, name='玉小剛'}}* Student{id=5, name='奧斯卡', teacher=Teacher{id=1, name='玉小剛'}}* Student{id=6, name='寧榮榮', teacher=Teacher{id=1, name='玉小剛'}}* Student{id=7, name='馬紅俊', teacher=Teacher{id=1, name='玉小剛'}}* Student{id=8, name='白塵香', teacher=Teacher{id=1, name='玉小剛'}}***/
}/*** @description: 2、 按照查詢嵌套處理 多對一* @author: zhengyuzhu* @date: 2023/11/22 23:07**/
@Test
public void testDemo2(){//第一步:獲得SqlSession對象SqlSession sqlSession = MybatisUtils.getSqlSession();StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);List<Student> studentList = studentMapper.getStudent2();for(Student student : studentList){System.out.println(student);}sqlSession.close();/*** 輸出如下:** [com.zyz.mybatis.mapper.StudentMapper.getStudent2]-==> Preparing: select * from tb_student;* [com.zyz.mybatis.mapper.StudentMapper.getStudent2]-==> Parameters:* [com.zyz.mybatis.mapper.StudentMapper.getTeacher]-====> Preparing: select * from tb_teacher where id = ?;* [com.zyz.mybatis.mapper.StudentMapper.getTeacher]-====> Parameters: 1(Integer)* [com.zyz.mybatis.mapper.StudentMapper.getTeacher]-<==== Total: 1* [com.zyz.mybatis.mapper.StudentMapper.getStudent2]-<== Total: 8* Student{id=1, name='唐三', teacher=Teacher{id=1, name='玉小剛'}}* Student{id=2, name='小舞', teacher=Teacher{id=1, name='玉小剛'}}* Student{id=3, name='戴沐白', teacher=Teacher{id=1, name='玉小剛'}}* Student{id=4, name='朱朱清', teacher=Teacher{id=1, name='玉小剛'}}* Student{id=5, name='奧斯卡', teacher=Teacher{id=1, name='玉小剛'}}* Student{id=6, name='寧榮榮', teacher=Teacher{id=1, name='玉小剛'}}* Student{id=7, name='馬紅俊', teacher=Teacher{id=1, name='玉小剛'}}* Student{id=8, name='白塵香', teacher=Teacher{id=1, name='玉小剛'}}***/
}
11、一對多處理
具體搭建過程同 多對一處理
1、實體類TeacherT,StudentT
同一個環境測試,實體類的字段有所不同。通過起別名的方式完善。
@Data //get,set
@NoArgsConstructor //無參構造
@AllArgsConstructor //有參構造
public class StudentT {private Integer id;private String name;private Integer tid;}@Data //get,set
@NoArgsConstructor //無參構造
@AllArgsConstructor //有參構造
public class TeacherT {private Integer id;private String name;private List<StudentT> studentTs;}
2、mybatis 核心配置文件
<typeAliases><package name="com.zyz.mybatis.entity" /><package name="com.zyz.mybatis.vo" /></typeAliases><mappers><mapper resource="com/zyz/mybatis/mapper/StudentMapper.xml"/><mapper resource="com/zyz/mybatis/mapper/TeacherMapper.xml"/></mappers>
3、mapper 接口
public interface TeacherMapper {/*** @description: 1、按照結果嵌套處理 多對一* @author: zhengyuzhu* @date: 2023/11/22 23:50 * @return: com.zyz.mybatis.vo.Teacher**/TeacherT getTeacherById(Integer id);/*** @description: 2、按照查詢嵌套處理 一對多* @author: zhengyuzhu* @date: 2023/11/23 0:12* @param: id* @return: com.zyz.mybatis.vo.TeacherT**/TeacherT getTeacher2(Integer id);}
4、mapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zyz.mybatis.mapper.TeacherMapper"><!--1、 按結果嵌套查詢--><select id="getTeacherById" resultMap="TeacherById">select t.id id, t.name tname, s.id sid,s.name sname,s.tid tidfrom tb_teacher tjoin tb_student son t.id = s.tid;</select><resultMap id="TeacherById" type="TeacherT"><result property="id" column="id"/><result property="name" column="tname"/><!--獲取List<Student>中的泛型使用 ofType--><collection property="studentTs" ofType="StudentT" javaType="java.util.List"><result property="id" column="sid"/><result property="name" column="sname"/><result property="tid" column="tid"/></collection></resultMap><!--2、按照查詢嵌套處理--><select id="getTeacher2" resultMap="TeacherStudent2">select * from tb_teacher where id = #{tid}</select><resultMap id="TeacherStudent2" type="TeacherT"><result property="id" column="id"/><collection property="studentTs" javaType="java.util.List" ofType="StudentT" select="getStudentByTeacherId" column="id"/></resultMap><select id="getStudentByTeacherId" resultType="StudentT">select * from tb_student where tid = #{tid}</select></mapper>
5、測試單元
/*** @description: 1、 按結果嵌套查詢 一對多* @author: zhengyuzhu* @date: 2023/11/22 23:07**/@Testpublic void testDemo3(){//第一步:獲得SqlSession對象SqlSession sqlSession = MybatisUtils.getSqlSession();TeacherMapper teacherMapper = sqlSession.getMapper(TeacherMapper.class);TeacherT teacher = teacherMapper.getTeacherById(1);System.out.println(teacher);sqlSession.close();/*** 輸出如下:*[org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 1486566962.[com.zyz.mybatis.mapper.TeacherMapper.getTeacherById]-==> Preparing: select t.id id, t.name tname, s.id sid,s.name sname,s.tid tid from tb_teacher t join tb_student s on t.id = s.tid;[com.zyz.mybatis.mapper.TeacherMapper.getTeacherById]-==> Parameters:[com.zyz.mybatis.mapper.TeacherMapper.getTeacherById]-<== Total: 8TeacherT(id=1, name=玉小剛, studentTs=[StudentT(id=1, name=唐三, tid=1),StudentT(id=2, name=小舞, tid=1),StudentT(id=3, name=戴沐白, tid=1),StudentT(id=4, name=朱朱清, tid=1),StudentT(id=5, name=奧斯卡, tid=1),StudentT(id=6, name=寧榮榮, tid=1),StudentT(id=7, name=馬紅俊, tid=1),StudentT(id=8, name=白塵香, tid=1)])[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@589b3632]***/}/*** @description: 2、 按照查詢嵌套處理 一對多* @author: zhengyuzhu* @date: 2023/11/22 23:07**/@Testpublic void testDemo4(){//第一步:獲得SqlSession對象SqlSession sqlSession = MybatisUtils.getSqlSession();TeacherMapper teacherMapper = sqlSession.getMapper(TeacherMapper.class);TeacherT teacher = teacherMapper.getTeacher2(1);System.out.println(teacher);sqlSession.close();/*** 輸出如下:*[org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 1486566962.[com.zyz.mybatis.mapper.TeacherMapper.getTeacher2]-==> Preparing: select * from tb_teacher where id = ?[com.zyz.mybatis.mapper.TeacherMapper.getTeacher2]-==> Parameters: 1(Integer)[com.zyz.mybatis.mapper.TeacherMapper.getStudentByTeacherId]-====> Preparing: select * from tb_student where tid = ?[com.zyz.mybatis.mapper.TeacherMapper.getStudentByTeacherId]-====> Parameters: 1(Integer)[com.zyz.mybatis.mapper.TeacherMapper.getStudentByTeacherId]-<==== Total: 8[com.zyz.mybatis.mapper.TeacherMapper.getTeacher2]-<== Total: 1TeacherT(id=1, name=玉小剛, studentTs=[StudentT(id=1, name=唐三, tid=1),StudentT(id=2, name=小舞, tid=1),StudentT(id=3, name=戴沐白, tid=1),StudentT(id=4, name=朱朱清, tid=1),StudentT(id=5, name=奧斯卡, tid=1),StudentT(id=6, name=寧榮榮, tid=1),StudentT(id=7, name=馬紅俊, tid=1),StudentT(id=8, name=白塵香, tid=1)])[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@589b3632]***/}
12、動態SQL
所謂的動態SQL,本質還是SQL語句,只是我們可以在SQL層面,去執行一個邏輯代碼
什么是動態SQL:動態SQL就是 指根據不同的條件生成不同的SQL語句
查詢搜索的時候用的多
12.1 搭建環境
大致過程
● 1、創建數據表
● 2、新建實體類
● 3、編寫實體類對應Mapper接口
● 4、和Mapper.XML文件
● 5、修改mybatis 核心配置文件
● 6、測試單元
1、創建數據表
CREATE TABLE `blog` (`id` int(10) NOT NULL COMMENT '博客id',`title` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '博客標題',`author` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '博客作者',`create_time` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '創建時間',`views` int(30) NOT NULL COMMENT '瀏覽量'
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
2、創建 Blog 實體類
@Data //get,set
@NoArgsConstructor //無參構造
@AllArgsConstructor //有參構造
public class Blog {private String id;private String title;private String author;private Date createTime; //屬性名和字段名不一致private int views;
}
3、編寫實體類對應Mapper接口
/*** @author zyz* @version 1.0* @data 2023/11/23 11:18* @Description:*/
public interface BlogMapper {/*** @description: IF的使用 查詢博客* @author: zhengyuzhu* @date: 2023/11/23 11:27* @param: map* @return: com.zyz.mybatis.entity.Blog**/Blog queryBlogIF(HashMap<String,Object> map);/*** @description: choose (when, otherwise)的使用 查詢博客* @author: zhengyuzhu* @date: 2023/11/23 11:30* @param: map * @return: com.zyz.mybatis.entity.Blog**/Blog queryBlogChoose(HashMap<String,Object> map);/*** @description: trim (where, set)的使用 查詢* @author: zhengyuzhu* @date: 2023/11/23 11:34* @param: map * @return: com.zyz.mybatis.entity.Blog**/Blog queryBlogIFTwo(HashMap<String,Object> map);/*** @description: trim (where, set)的使用 修改* @author: zhengyuzhu* @date: 2023/11/23 11:35* @param: map **/void updateBlog(HashMap<String,Object> map);/*** @description: Foreach 的使用* @author: zhengyuzhu* @date: 2023/11/23 14:02* @param: map * @return: java.util.List<com.zyz.mybatis.entity.Blog>**/List<Blog> queryBlogForeach(HashMap<String,Object> map);/*** @description: IF的使用 使用代碼片段* @author: zhengyuzhu* @date: 2023/11/23 11:27* @param: map* @return: com.zyz.mybatis.entity.Blog**/Blog queryBlogIFThree(HashMap<String,Object> map);}
4、修改mybatis 核心配置文件
<!-- 1、使用映射器 --><mappers><mapper resource="com/zyz/mybatis/mapper/BlogMapper.xml"/></mappers>
具體的動態SQL 編寫以及具體的 測試單元分成如下小節
12.2 IF
mapper.xml 文件
由于 實體類 和 數據庫 字段不一致,這里進行了映射
<!-- 結果集映射 --><resultMap id="BlogMap" type="Blog"><!--column數據庫中的字段,property實體類中的屬性--><result column="id" property="id" /><result column="title" property="title" /><result column="author" property="author" /><result column="create_time" property="createTime" /><result column="views" property="views" /></resultMap><!--IF的使用 查詢博客--><select id="queryBlogIF" parameterType="map" resultMap="BlogMap">select * from blog where 1=1<if test="title != null">and title = #{title}</if><if test="author != null">and author = #{author}</if></select>
測試單元
/*** @description: IF 測試* @author: zhengyuzhu* @date: 2023/11/23 12:46**/
@Test
public void testDemo1(){//第一步:獲得SqlSession對象SqlSession sqlSession = MybatisUtils.getSqlSession();BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);HashMap<String, Object> blogMap = new HashMap<>();blogMap.put("title","張麻子娶親");blogMap.put("author","張老頭");Blog blog = blogMapper.queryBlogIF(blogMap);//查詢System.out.println(blog);sqlSession.close();/***** blogMap.put("title","張麻子娶親");* blogMap.put("author","張老頭");** 查詢條件兩個:輸出如下:** [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 846947180.* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIF]-==> Preparing: select * from blog where 1=1 and title = ? and author = ?* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIF]-==> Parameters: 張麻子娶親(String), 張老頭(String)* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIF]-<== Total: 1* Blog(id=1, title=張麻子娶親, author=張老頭, createTime=2023-11-23 00:00:00, views=30)* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@327b636c]*** blogMap.put("title","張麻子娶親");* 查詢條件一個輸出如下** [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 1172131546.* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIF]-==> Preparing: select * from blog where 1=1 and title = ?* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIF]-==> Parameters: 張麻子娶親(String)* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIF]-<== Total: 1* Blog(id=1, title=張麻子娶親, author=張老頭, createTime=2023-11-23 00:00:00, views=30)* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@45dd4eda]******/
}
12.3 choose (when, otherwise)
mapper.xml 文件
這里也進行了結果集映射,如上
<!--choose (when, otherwise)的使用 查詢博客-->
<select id="queryBlogChoose" parameterType="map" resultMap="BlogMap">select * from blog<where><choose><when test="title != null">title = #{title}</when><when test="author != null">and author = #{author}</when><otherwise>and views = #{views}</otherwise></choose></where>
</select>
測試單元
/*** @description: choose 測試 when otherwise* @author: zhengyuzhu* @date: 2023/11/23 13:34**/@Testpublic void testDemo2(){//第一步:獲得SqlSession對象SqlSession sqlSession = MybatisUtils.getSqlSession();BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);HashMap<String, Object> blogMap = new HashMap<>();// blogMap.put("title","張麻子娶親");// blogMap.put("author","張老頭");blogMap.put("views",30);Blog blog = blogMapper.queryBlogChoose(blogMap);System.out.println(blog);sqlSession.close();/**** 1、多個參數不會拼接,哪個有值 用哪個** blogMap.put("title","張麻子娶親");* blogMap.put("author","張老頭");** 查詢條件兩個 但是拼接的時候 第一個有值,就不會在拼接接下來的數據** [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 846947180.* [com.zyz.mybatis.mapper.BlogMapper.queryBlogChoose]-==> Preparing: select * from blog WHERE title = ?* [com.zyz.mybatis.mapper.BlogMapper.queryBlogChoose]-==> Parameters: 張麻子娶親(String)* [com.zyz.mybatis.mapper.BlogMapper.queryBlogChoose]-<== Total: 1* Blog(id=1, title=張麻子娶親, author=張老頭, createTime=2023-11-23 00:00:00, views=30)* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@327b636c]*** blogMap.put("author","張老頭");* 2、查詢條件一個輸出如下*[org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 1172131546.[com.zyz.mybatis.mapper.BlogMapper.queryBlogChoose]-==> Preparing: select * from blog WHERE author = ?[com.zyz.mybatis.mapper.BlogMapper.queryBlogChoose]-==> Parameters: 張老頭(String)[com.zyz.mybatis.mapper.BlogMapper.queryBlogChoose]-<== Total: 1Blog(id=1, title=張麻子娶親, author=張老頭, createTime=2023-11-23 00:00:00, views=30)[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@45dd4eda]**** 3、查詢條件 一個 都不滿足情況 otherwise* blogMap.put("views",30);** [com.zyz.mybatis.mapper.BlogMapper.queryBlogChoose]-==> Preparing: select * from blog WHERE views = ?* [com.zyz.mybatis.mapper.BlogMapper.queryBlogChoose]-==> Parameters: 30(Integer)* [com.zyz.mybatis.mapper.BlogMapper.queryBlogChoose]-<== Total: 1* Blog(id=1, title=張麻子娶親, author=張老頭, createTime=2023-11-23 00:00:00, views=30)* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@45dd4eda]***/}
12.4 trim (where, set)
mapper.xml 文件
這里也進行了結果集映射,如上
<!--trim (where, set)的使用 查詢--><select id="queryBlogIFTwo" parameterType="map" resultMap="BlogMap">select * from blog<where><if test="title != null">and title = #{title}</if><if test="author != null">and author = #{author}</if></where></select><!--trim (where, set)的使用 修改--><update id="updateBlog" parameterType="map">update blog<set><if test="title != null">title = #{title},</if><if test="author != null">author = #{author}</if></set>where id = #{id}</update>
測試單元
/*** @description: trim (where, set)的使用 查詢* @author: zhengyuzhu* @date: 2023/11/23 13:36**/
@Test
public void testDemo3(){//第一步:獲得SqlSession對象SqlSession sqlSession = MybatisUtils.getSqlSession();BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);HashMap<String, Object> blogMap = new HashMap<>();blogMap.put("title","張麻子娶親");blogMap.put("author","張老頭");Blog blog = blogMapper.queryBlogIFTwo(blogMap);//查詢System.out.println(blog);sqlSession.close();/***** blogMap.put("title","張麻子娶親");* blogMap.put("author","張老頭");* 查詢條件兩個:輸出如下:** [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 846947180.* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIFTwo]-==> Preparing: select * from blog WHERE title = ? and author = ?* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIFTwo]-==> Parameters: 張麻子娶親(String), 張老頭(String)* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIFTwo]-<== Total: 1* Blog(id=1, title=張麻子娶親, author=張老頭, createTime=2023-11-23 00:00:00, views=30)* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@327b636c]*** blogMap.put("title","張麻子娶親");* 查詢條件一個輸出如下** [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 1172131546.* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIF]-==> Preparing: select * from blog where 1=1 and title = ?* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIF]-==> Parameters: 張麻子娶親(String)* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIF]-<== Total: 1* Blog(id=1, title=張麻子娶親, author=張老頭, createTime=2023-11-23 00:00:00, views=30)* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@45dd4eda]******/
}/*** @description: trim (where, set)的使用 修改* @author: zhengyuzhu* @date: 2023/11/23 13:36**/
@Test
public void testDemo4(){//第一步:獲得SqlSession對象SqlSession sqlSession = MybatisUtils.getSqlSession();BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);HashMap<String, Object> blogMap = new HashMap<>();blogMap.put("title","張麻子娶親");blogMap.put("author","張老頭");Blog blog = blogMapper.queryBlogIFTwo(blogMap);//查詢System.out.println("修改前的數據:" + blog);HashMap<String, Object> blogMapTwo = new HashMap<>();blogMapTwo.put("id",1);blogMapTwo.put("title","如何學號java ?");blogMapTwo.put("author","小明");Blog blog1 = new Blog();blog1.setAuthor("如何學號java ?");blog1.setAuthor("小明");blogMapper.updateBlog(blogMapTwo);//修改HashMap<String, Object> blogMap3 = new HashMap<>();blogMap3.put("title","如何學號java ?");Blog blog2 = blogMapper.queryBlogIFTwo(blogMap3);//查詢System.out.println("修改后的數據:" + blog2);sqlSession.close();/***** blogMapTwo.put("id",1);* blogMapTwo.put("title","如何學號java ?");* blogMapTwo.put("author","小明");* 查詢條件兩個:輸出如下:** [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 846947180.* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIFTwo]-==> Preparing: select * from blog WHERE title = ? and author = ?* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIFTwo]-==> Parameters: 張麻子娶親(String), 張老頭(String)* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIFTwo]-<== Total: 1* 修改前的數據:Blog(id=1, title=張麻子娶親, author=張老頭, createTime=2023-11-23 00:00:00, views=30)* [com.zyz.mybatis.mapper.BlogMapper.updateBlog]-==> Preparing: update blog SET title = ?, author = ? where id = ?* [com.zyz.mybatis.mapper.BlogMapper.updateBlog]-==> Parameters: 如何學號java ?(String), 小明(String), 1(Integer)* [com.zyz.mybatis.mapper.BlogMapper.updateBlog]-<== Updates: 1* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIFTwo]-==> Preparing: select * from blog WHERE title = ?* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIFTwo]-==> Parameters: 如何學號java ?(String)* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIFTwo]-<== Total: 1* 修改后的數據:Blog(id=1, title=如何學號java ?, author=小明, createTime=2023-11-23 00:00:00, views=30)* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@327b636c]*****/
}
12.5 foreach
更詳細的資料參考:mybatis之foreach用法
foreach元素的屬性主要有item,index,collection,open,separator,close。
● item:集合中元素迭代時的別名,該參數為必選。
● index:在list和數組中,index是元素的序號,在map中,index是元素的key,該參數可選
● open:foreach代碼的開始符號,一般是(和close=")“合用。常用在in(),values()時。該參數可選
● separator:元素之間的分隔符,例如在in()的時候,separator=”,“會自動在元素中間用“,“隔開,避免手動輸入逗號導致sql錯誤,如in(1,2,)這樣。該參數可選。
● close: foreach代碼的關閉符號,一般是)和open=”("合用。常用在in(),values()時。該參數可選。
● collection: 要做foreach的對象,作為入參時,List對象默認用"list"代替作為鍵,數組對象有"array"代替作為鍵,Map對象沒有默認的鍵。當然在作為入參時可以使用@Param(“keyName”)來設置鍵,設置keyName后,list,array將會失效。 除了入參這種情況外,還有一種作為參數對象的某個字段的時候。舉個例子:如果User有屬性List ids。入參是User對象,那么這個collection = “ids”.如果User有屬性Ids ids;其中Ids是個對象,Ids有個屬性List id;入參是User對象,那么collection = “ids.id”
在使用foreach的時候最關鍵的也是最容易出錯的就是collection屬性,該屬性是必須指定的,但是在不同情況下,該屬性的值是不一樣的,主要有一下3種情況:
● 如果傳入的是單參數且參數類型是一個List的時候,collection屬性值為list .
● 如果傳入的是單參數且參數類型是一個array數組的時候,collection的屬性值為array .
● 如果傳入的參數是多個的時候,我們就需要把它們封裝成一個Map了,當然單參數也可以封裝成map,實際上如果你在傳入參數的時候,在MyBatis里面也是會把它封裝成一個Map的,map的key就是參數名,所以這個時候collection屬性值就是傳入的List或array對象在自己封裝的map里面的key.
針對最后一條,我們來看一下官方說法:
注意 你可以將一個 List 實例或者數組作為參數對象傳給 MyBatis,當你這么做的時候,MyBatis 會自動將它包裝在一個 Map 中并以名稱為鍵。List 實例將會以“list”作為鍵,而數組實例的鍵將是“array”。
所以,不管是多參數還是單參數的list,array類型,都可以封裝為map進行傳遞。如果傳遞的是一個List,則mybatis會封裝為一個list為key,list值為object的map,如果是array,則封裝成一個array為key,array的值為object的map,如果自己封裝呢,則colloection里放的是自己封裝的map里的key值。
mapper.xml 文件
這里也進行了結果集映射
<!--foreach元素的屬性主要有item,index,collection,open,separator,close。● item:集合中元素迭代時的別名,該參數為必選。● index:在list和數組中,index是元素的序號,在map中,index是元素的key,該參數可選● open:foreach代碼的開始符號,一般是(和close=")"合用。常用在in(),values()時。該參數可選● separator:元素之間的分隔符,例如在in()的時候,separator=","會自動在元素中間用“,“隔開,避免手動輸入逗號導致sql錯誤,如in(1,2,)這樣。該參數可選。● close: foreach代碼的關閉符號,一般是)和open="("合用。常用在in(),values()時。該參數可選。● collection: 要做foreach的對象,作為入參時,List對象默認用"list"代替作為鍵,數組對象有"array"代替作為鍵,Map對象沒有默認的鍵。當然在作為入參時可以使用@Param("keyName")來設置鍵,設置keyName后,list,array將會失效。 除了入參這種情況外,還有一種作為參數對象的某個字段的時候。舉個例子:如果User有屬性List ids。入參是User對象,那么這個collection = "ids".如果User有屬性Ids ids;其中Ids是個對象,Ids有個屬性List id;入參是User對象,那么collection = "ids.id"select * from blog where 1=1 and (id=1 or id=2 or id=3)我們現在傳遞一個萬能的map,這map中可以存在一個集合!--><select id="queryBlogForeach" parameterType="map" resultMap="BlogMap">select * from blog<where><foreach collection="ids" item="id" open="and (" close=")" separator="or">id = #{id}</foreach></where></select>
測試單元
/*** @description: Foreach 的使用* @author: zhengyuzhu* @date: 2023/11/23 14:03**/@Testpublic void testDemo5(){//第一步:獲得SqlSession對象SqlSession sqlSession = MybatisUtils.getSqlSession();BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);ArrayList<Object> idList = new ArrayList<>();idList.add(1);idList.add(2);idList.add(3);HashMap<String, Object> blogMap = new HashMap<>();blogMap.put("ids",idList);List<Blog> blogList = blogMapper.queryBlogForeach(blogMap);//查詢for(Blog blog : blogList){System.out.println(blog);}sqlSession.close();/***** 查詢條件兩個:輸出如下:** [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 360062456.* [com.zyz.mybatis.mapper.BlogMapper.queryBlogForeach]-==> Preparing: select * from blog WHERE ( id = ? or id = ? or id = ? )* [com.zyz.mybatis.mapper.BlogMapper.queryBlogForeach]-==> Parameters: 1(Integer), 2(Integer), 3(Integer)* [com.zyz.mybatis.mapper.BlogMapper.queryBlogForeach]-<== Total: 3* Blog(id=1, title=如何學號java ?, author=小明, createTime=2023-11-23 00:00:00, views=30)* Blog(id=2, title=張麻子學java, author=李老頭, createTime=2023-11-22 00:00:00, views=560)* Blog(id=3, title=張麻子學數據庫, author=米老頭, createTime=2023-11-22 00:00:00, views=760)* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@15761df8]*****/}
12.5 代碼片段(復用)
有的時候,我們可以能會將一些功能的部分抽取出來,方便復用!
- 使用SQL標簽抽取公共的部分
<sql id="if-title-author"><if test="title != null">title = #{title}</if><if test="author != null">and author = #{author}</if></sql>
- 在需要使用的地方使用Include標簽引用即可
這里有對結果集進行映射
<!--復用 SQL代碼片段的方式-->
<select id="queryBlogIFThree" parameterType="map" resultMap="BlogMap">select * from blog<where><include refid="if-title-author"></include></where>
</select>
測試單元
/*** @description: IF 的使用 復用代碼片段* @author: zhengyuzhu* @date: 2023/11/23 14:33 **/@Testpublic void testDemo6(){//第一步:獲得SqlSession對象SqlSession sqlSession = MybatisUtils.getSqlSession();BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class);HashMap<String, Object> blogMap = new HashMap<>();blogMap.put("title","張麻子娶親");blogMap.put("author","張老頭");Blog blog = blogMapper.queryBlogIF(blogMap);//查詢System.out.println(blog);sqlSession.close();/***** blogMap.put("title","張麻子娶親");* blogMap.put("author","張老頭");** 查詢條件兩個:輸出如下:** [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 846947180.* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIF]-==> Preparing: select * from blog where 1=1 and title = ? and author = ?* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIF]-==> Parameters: 張麻子娶親(String), 張老頭(String)* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIF]-<== Total: 1* Blog(id=1, title=張麻子娶親, author=張老頭, createTime=2023-11-23 00:00:00, views=30)* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@327b636c]*** blogMap.put("title","張麻子娶親");* 查詢條件一個輸出如下** [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 1172131546.* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIF]-==> Preparing: select * from blog where 1=1 and title = ?* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIF]-==> Parameters: 張麻子娶親(String)* [com.zyz.mybatis.mapper.BlogMapper.queryBlogIF]-<== Total: 1* Blog(id=1, title=張麻子娶親, author=張老頭, createTime=2023-11-23 00:00:00, views=30)* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@45dd4eda]******/}
13、緩存
13.1 簡介
1、什么是緩存[Cache]?
● 存在內存中的臨時數據。
● 將用戶經常查詢的數據放在緩存(內存)中,用戶去查詢數據就不用從磁盤上(關系型數據庫查詢文件)查詢,從緩存中查詢,從而提高查詢效率,解決了高并發系統的性能問題。
-
為什么使用緩存?
● 減少和數據庫的交互次數,減少系統開銷,提高系統效率。 -
什么樣的數據能使用緩存?
● 經常查詢并且不經常改變的數據。【可以使用緩存】
資料參考:mybatis一級緩存二級緩存
MYSQL緩存:一級緩存和二級緩存
13.2 Mybatis緩存
● Mybatis包含一個非常強大的查詢緩存特性,它可以非常方便地定制和配置緩存。緩存可以極大的提升查詢效率。
● Mybatis系統中默認定義了兩級緩存:一級緩存和二級緩存
1、默認情況下,只有一級緩存開啟。(SqlSession級別的緩存,也稱為本地緩存)
2、二級緩存需要手動開啟和配置,它是基于namespace級別的緩存。
3、為了提高擴展性,Mybatis定義了緩存接口Cache,我們可以通過實現Cache接口來自 定義二級緩存。
13.2.1 一級緩存
13.2.1.1 原理說明
Mybatis對緩存提供支持,但是在沒有配置的默認情況下,它只開啟一級緩存,一級緩存只是相對于同一個SqlSession而言。所以在參數和SQL完全一樣的情況下,我們使用同一個SqlSession對象調用一個Mapper方法,往往只執行一次SQL,因為使用SelSession第一次查詢后,MyBatis會將其放在緩存中,以后再查詢的時候,如果沒有聲明需要刷新,并且緩存沒有超時的情況下,SqlSession都會取出當前緩存的數據,而不會再次發送SQL到數據庫。
為什么要使用一級緩存,不用多說也知道個大概。但是還有幾個問題我們要注意一下。
1、一級緩存的生命周期有多長?
a、MyBatis在開啟一個數據庫會話時,會 創建一個新的SqlSession對象,SqlSession對象中會有一個新的Executor對象。Executor對象中持有一個新的PerpetualCache對象;當會話結束時,SqlSession對象及其內部的Executor對象還有PerpetualCache對象也一并釋放掉。
b、如果SqlSession調用了close()方法,會釋放掉一級緩存PerpetualCache對象,一級緩存將不可用。
c、如果SqlSession調用了clearCache(),會清空PerpetualCache對象中的數據,但是該對象仍可使用。
d、SqlSession中執行了任何一個update操作(update()、delete()、insert()) ,都會清空PerpetualCache對象的數據,但是該對象可以繼續使用
2、怎么判斷某兩次查詢是完全相同的查詢?
mybatis認為,對于兩次查詢,如果以下條件都完全一樣,那么就認為它們是完全相同的兩次查詢。
2.1 傳入的statementId
2.2 查詢時要求的結果集中的結果范圍
2.3. 這次查詢所產生的最終要傳遞給JDBC java.sql.Preparedstatement的Sql語句字符串(boundSql.getSql() )
2.4 傳遞給java.sql.Statement要設置的參數值
13.2.1.2 緩存成功
測試步驟:
- 開啟日志!
- 測試在一個Session中查詢兩次相同記錄
- 查看日志輸出
/*** @description: 測試緩存 同一個session 連續查詢兩次* @author: zhengyuzhu* @date: 2023/11/23 15:02**/@Testpublic void test07(){//第一步:獲得SqlSession對象SqlSession sqlSession = MybatisUtils.getSqlSession();//方式一:getMapperStudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);Student student = studentMapper.queryStudentById(1);System.out.println(student);System.out.println("-----------------");Student student1 = studentMapper.queryStudentById(1);System.out.println(student1);//關閉SqlSessionsqlSession.close();/*** 只進行了一次查詢 輸出如下 :** [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 341748265.* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@145eaa29]* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Preparing: select * from student where stuNo = ?* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Parameters: 1(Integer)* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-<== Total: 1* Student{stuNo=1, stuName='張三', cardID=1115, classID=1}* -----------------* Student{stuNo=1, stuName='張三', cardID=1115, classID=1}* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@145eaa29]* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@145eaa29]***/}
13.2.1.3 緩存失效的情況
一級緩存失效的情況:
- 查詢不同的東西;
- 增刪改操作,可能會改變原來的數據,所以必定會刷新緩存!
- 查詢不同的Mapper.xml
- 手動清理緩存!
/*** @description: 測試緩存失效的情況 1、進行不同的數據查詢* @author: zhengyuzhu* @date: 2023/11/23 15:02**/@Testpublic void test08(){//第一步:獲得SqlSession對象SqlSession sqlSession = MybatisUtils.getSqlSession();//方式一:getMapperStudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);Student student = studentMapper.queryStudentById(1);System.out.println(student);System.out.println("-----------------");Student student1 = studentMapper.queryStudentById(2);System.out.println(student1);//關閉SqlSessionsqlSession.close();/*** 1、查詢不同的數據的時候,緩存會失效** [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Opening JDBC Connection* [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 341748265.* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@145eaa29]* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Preparing: select * from student where stuNo = ?* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Parameters: 1(Integer)* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-<== Total: 1* Student{stuNo=1, stuName='張三', cardID=1115, classID=1}* -----------------* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Preparing: select * from student where stuNo = ?* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Parameters: 2(Integer)* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-<== Total: 1* Student{stuNo=2, stuName='李四', cardID=1116, classID=2}* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@145eaa29]* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@145eaa29]***/}/*** @description: 測試緩存失效的情況 2、進行了刪除或者修改操作* @author: zhengyuzhu* @date: 2023/11/23 15:02**/@Testpublic void test09(){//第一步:獲得SqlSession對象SqlSession sqlSession = MybatisUtils.getSqlSession();//方式一:getMapperStudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);Student student1 = studentMapper.queryStudentById(1);System.out.println("修改前數據:" + student1);System.out.println("-----------------");Student student = new Student();student.setStuNo(1);student.setStuName("張麻子666");student.setClassID(8);student.setCardID(8);studentMapper.updateStudent(student); //修改System.out.println("-----------------");Student student2 = studentMapper.queryStudentById(1);System.out.println("修改后數據:" + student2);//關閉SqlSessionsqlSession.close();/*** 1、查詢相同的數據,但是中間進行了修改操作** [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Opening JDBC Connection* [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 341748265.* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@145eaa29]* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Preparing: select * from student where stuNo = ?* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Parameters: 1(Integer)* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-<== Total: 1* 修改前數據:Student{stuNo=1, stuName='張三', cardID=1115, classID=1}* -----------------* [com.zyz.mybatis.mapper.StudentMapper.updateStudent]-==> Preparing: update student set stuNo = ?, stuName = ?, cardid = ?, classid = ? where stuNo = ?* [com.zyz.mybatis.mapper.StudentMapper.updateStudent]-==> Parameters: 1(Integer), 張麻子666(String), 8(Integer), 8(Integer), 1(Integer)* [com.zyz.mybatis.mapper.StudentMapper.updateStudent]-<== Updates: 1* -----------------* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Preparing: select * from student where stuNo = ?* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Parameters: 1(Integer)* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-<== Total: 1* 修改后數據:Student{stuNo=1, stuName='張麻子666', cardID=8, classID=8}* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Rolling back JDBC Connection [com.mysql.jdbc.JDBC4Connection@145eaa29]* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@145eaa29]* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@145eaa29]***/}
手動清理緩存!
sqlSession.clearCache(); //手動清除緩存
/*** @description: 手動清除緩存 緩存失效。查詢兩次* @author: zhengyuzhu* @date: 2023/11/23 15:21**/
@Test
public void test10(){//第一步:獲得SqlSession對象SqlSession sqlSession = MybatisUtils.getSqlSession();//方式一:getMapperStudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);Student student = studentMapper.queryStudentById(1);System.out.println(student);System.out.println("-----------------");sqlSession.clearCache(); //手動清除緩存Student student1 = studentMapper.queryStudentById(1);System.out.println(student1);//關閉SqlSessionsqlSession.close();/*** 1、同一個連接,連續兩次查詢。第二次查詢拿緩存數據** [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 341748265.* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@145eaa29]* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Preparing: select * from student where stuNo = ?* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Parameters: 1(Integer)* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-<== Total: 1* Student{stuNo=1, stuName='張三', cardID=1115, classID=1}* -----------------* Student{stuNo=1, stuName='張三', cardID=1115, classID=1}* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@145eaa29]* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@145eaa29]**** 2、手動清除緩存** [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Opening JDBC Connection* [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 341748265.* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@145eaa29]* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Preparing: select * from student where stuNo = ?* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Parameters: 1(Integer)* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-<== Total: 1* Student{stuNo=1, stuName='張三', cardID=1115, classID=1}* -----------------* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Preparing: select * from student where stuNo = ?* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Parameters: 1(Integer)* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-<== Total: 1* Student{stuNo=1, stuName='張三', cardID=1115, classID=1}* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@145eaa29]* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@145eaa29]***/}
擴展
@Testpublic void test01() throws IOException {SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();SqlSession session = sqlSessionFactory.openSession();EmployeeMapper mapper = session.getMapper(EmployeeMapper.class);try {Employee map = mapper.getEmployeeById(1);session.clearCache();Employee map2 = mapper.getEmployeeById(1);System.out.println(map == map2);session.commit();} finally {session.close();}}
輸出結果為false.
因為手動清清除緩存,緩存失效
13.2.2 二級緩存
13.2.2.1 原理說明
MyBatis的二級緩存是Application級別的緩存,它可以提高對數據庫查詢的效率,以提高應用的性能。
SqlSessionFactory層面上的二級緩存默認是不開啟的,二級緩存的開席需要進行配置,實現二級緩存的時候,MyBatis要求返回的POJO必須是可序列化的。 也就是要求實現Serializable接口,配置方法很簡單,只需要在映射XML文件配置就可以開啟緩存了,如果我們配置了二級緩存就意味著:
● 映射語句文件中的所有select語句將會被緩存。
● 映射語句文件中的所欲insert、update和delete語句會刷新緩存。
● 緩存會使用默認的Least Recently Used(LRU,最近最少使用的)算法來收回。
● 根據時間表,比如No Flush Interval,(CNFI沒有刷新間隔),緩存不會以任何時間順序來刷新。
● 緩存會存儲列表集合或對象(無論查詢方法返回什么)的1024個引用
● 緩存會被視為是read/write(可讀/可寫)的緩存,意味著對象檢索不是共享的,而且可以安全的被調用者修改,不干擾其他調用者或線程所做的潛在修改。
13.2.2.1 詳細說明
詳細說明:
二級緩存:全局緩存;基于namespace級別的緩存。一個namespace對應一個二級緩存。
工作機制:1.一個會話,查詢一條數據,這個數據會被放在當前會話的一級緩存中。
2,如果會話被關閉了,一級緩存中的數據會被保存帶二級緩存。新的會話查詢信息就會參照二級緩存。
3.sqlSession > Employee>employee
sqlSession >DepartmentMapper=>Department
不同的namespace查出的數據會放在自己對應的緩存中。
效果:查出的數據首先放在一級緩存中,只有一級緩存被關閉或者提交以后,一級緩存數據才會轉移到二級緩存
使用步驟:
1.開啟全局緩存配置。
2.因為是namespace級別,需要搭配每個xxxMapper.xml中配置二級緩存
<cache flushInterval="60000" size="512" readOnly="true" eviction="FIFO" type="" />
eviction:緩存的回收策略:
LRU – 最近最少使用的:移除最長時間不被使用的對象。
FIFO – 先進先出:按對象進入緩存的順序來移除它們。
SOFT – 軟引用:移除基于垃圾回收器狀態和軟引用規則的對象。
WEAK – 弱引用:更積極地移除基于垃圾收集器狀態和弱引用規則的對象。
flushInterval:緩存刷新間隔。緩存多久清空一次,默認不清空。設置一個毫秒值。
readOnly:是否只讀。true:mybatis認為所有從緩存中獲取數據的操作都是只讀操作,不會修改數據。
mybatis為了加快獲取速度,直接就會將數據在緩存中的引用交給用戶。不安全,速度快。false:mybatis覺得獲取的數據可能被修改。mybatis會利用序列化和反序列化的技術克隆一份新的數據給用戶。安全,速度快。
size:緩存放多少元素。
type:指定自定義緩存全類名。實現cache接口即可。
3.pojo需要實現序列換接口。
和緩存相關的配置/屬性:
1.cacheEnabled:如果是false,關閉二級緩存,不關閉一級緩存。
2.每個select標簽都有userCache="true"屬性:對一級緩存沒有影響。設置為false,二級緩存失效。
3.每個增刪改標簽都有flushCache="true"屬性:一級緩存和二級緩存都會被清空。
4.在查詢標簽中flushCache="false"屬性:如果設置為true,查完會清空,一級二級緩存都會被清空,都不會用緩存。
5.sqlSession.clearn():跟session有關,只會清除一級緩存。
6.localCacheScope:<settings><setting name="localCacheScope" value="SESSION"/></settings>本地緩存作用域。
一級緩存SESSION:當前會話的所有數據保存到回話緩存中。STATEMENT:禁用一級緩存。
緩存首先一進來去查二級緩存,二級緩存沒有去找一級緩存,一級緩存沒有去找數據庫。二級緩存----->一級緩存-------->數據庫。
自定義緩存 implements Cache,重寫接口中的保存等方法,比如說保存到redis.
13.2.2.2 測試案例
步驟:
- 在mybatis-config.xml開啟全局緩存
<settings><setting name="cacheEnabled" value="true"/></settings>
- 在要使用二級緩存的Mapper中開啟
<!--在當前Mapper.xml中使用二級緩存--><cache/>
也可以自定義參數<!--開啟本mapper的namespace下的二級緩存--><!--eviction:代表的是緩存回收策略,目前MyBatis提供以下策略。(1) LRU,最近最少使用的,一處最長時間不用的對象(2) FIFO,先進先出,按對象進入緩存的順序來移除他們(3) SOFT,軟引用,移除基于垃圾回收器狀態和軟引用規則的對象(4) WEAK,弱引用,更積極的移除基于垃圾收集器狀態和弱引用規則的對象。這里采用的是LRU,移除最長時間不用的對形象flushInterval:刷新間隔時間,單位為毫秒,這里配置的是100秒刷新,如果你不配置它,那么當SQL被執行的時候才會去刷新緩存。size:引用數目,一個正整數,代表緩存最多可以存儲多少個對象,不宜設置過大。設置過大會導致內存溢出。這里配置的是1024個對象readOnly:只讀,意味著緩存數據只能讀取而不能修改,這樣設置的好處是我們可以快速讀取緩存,缺點是我們沒有辦法修改緩存,他的默認值是false,不允許我們修改--><cache eviction="LRU" flushInterval="100000" readOnly="true" size="1024"/><!--在當前Mapper.xml中使用二級緩存:1、默認:<cache/>2、自定義參數 <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>-->
測試
- 問題:如果沒有自定義參數,則會報錯,我們需要將實體類序列化
Cause: java.io.NotSerializableException: com.zyz.mybatis.entity.Student
小結:
● 只要開啟了二級緩存,在同一個Mapper下就有效;
● 所有的數據都會先放在一級緩存中;
● 只有當會話提交或者關閉的時候,才會提交到二級緩存中!
單元測試
一定要提交或者關閉
/*** @description: 二級緩存 創建兩個sqlSession* @author: zhengyuzhu* @date: 2023/11/23 15:51**/@Testpublic void test11(){SqlSessionFactory sqlSessionFactory = null;try {//使用Mybatis第一步:獲取sqlSessionFactory對象String resource = "mybatis-config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);} catch (Exception e) {e.printStackTrace();}//第一步:獲得SqlSession對象SqlSession sqlSession = sqlSessionFactory.openSession();//方式一:getMapperStudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);Student student1 = studentMapper.queryStudentById(1);System.out.println("第1次查詢:" + student1);Student student2 = studentMapper.queryStudentById(1);System.out.println("第2次查詢:" + student2);// 效果:查出的數據首先放在一級緩存中,只有一級緩存被關閉或者提交以后,// 一級緩存數據才會轉移到二級緩存sqlSession.commit();System.out.println("二級緩存觀測點");SqlSession sqlSession2 = sqlSessionFactory.openSession();StudentMapper studentMapper2 = sqlSession2.getMapper(StudentMapper.class);Student student3 = studentMapper2.queryStudentById(1);System.out.println("第一次執行:"+ student3);Student student4 = studentMapper2.queryStudentById(1);System.out.println("第2次執行:"+ student4);sqlSession2.commit();System.out.println("四個對象是否相同:" + ((student1 == student2) && (student3 == student4) &&(student1 == student4)));//關閉SqlSessionsqlSession.close();sqlSession2.close();/*** 二級緩存開啟** [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Opening JDBC Connection* [org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 921760190.* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@36f0f1be]* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Preparing: select * from student where stuNo = ?* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-==> Parameters: 1(Integer)* [com.zyz.mybatis.mapper.StudentMapper.queryStudentById]-<== Total: 1* 第1次查詢:Student{stuNo=1, stuName='張三', cardID=1115, classID=1}* [com.zyz.mybatis.mapper.StudentMapper]-Cache Hit Ratio [com.zyz.mybatis.mapper.StudentMapper]: 0.0* 第2次查詢:Student{stuNo=1, stuName='張三', cardID=1115, classID=1}* 二級緩存觀測點* [com.zyz.mybatis.mapper.StudentMapper]-Cache Hit Ratio [com.zyz.mybatis.mapper.StudentMapper]: 0.3333333333333333* 第一次執行:Student{stuNo=1, stuName='張三', cardID=1115, classID=1}* [com.zyz.mybatis.mapper.StudentMapper]-Cache Hit Ratio [com.zyz.mybatis.mapper.StudentMapper]: 0.5* 第2次執行:Student{stuNo=1, stuName='張三', cardID=1115, classID=1}* 四個對象是否相同:true* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@36f0f1be]* [org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@36f0f1be]**/}