如果對一些基礎理論感興趣可以看這一期👇
SSM【Spring SpringMVC Mybatis】——Mybatis
目錄
1、Mybatis中參數傳遞問題
1.1 單個普通參數
1.2 多個普通參數
1.3 命名參數
?1.4 POJO參數
1.5 Map參數
1.6 Collection|List|Array等參數
2、Mybatis參數傳遞【#與$區別】
2.1 回顧JDBC
2.2 #與$區別
2.3 #與$使用場景
3、Mybatis查詢中返回值四種情況
3.1 查詢單行數據返回單個對象
3.2 查詢多行數據返回對象的集合
3.3 查詢單行數據返回Map集合
3.4 查詢多行數據返回Map集合
4、Mybatis中自動映射與自定義映射
4.1 自動映射與自定義映射
4.2 自定義映射-級聯映射
4.3 自定義映射-association映射
4.4 自定義映射-collection映射
4.5 ResultMap相關標簽及屬性
4.6 Mybatis中分步查詢
4.7 Mybatis延遲加載【懶加載】
5、Mybatis動態SQL【重點】
5.1 動態SQL概述
5.2 常用標簽
5.3 示例代碼
6、 Mybatis中緩存機制
6.1 緩存概述
6.2 Mybatis中的緩存概述
6.3 Mybatis緩存機制之一級緩存
6.4 Mybatis緩存機制之二級緩存
6.5 Mybatis中緩存機制之第三方緩存
1、Mybatis中參數傳遞問題
1.1 單個普通參數
可以任意使用:參數數據類型、參數名稱不用考慮
1.2 多個普通參數
Mybatis底層封裝Map結構,封裝key為param1、param2....【支持:arg0、arg1、...】
1.3 命名參數
語法:
@Param(value="參數名")@Param("參數名")
位置:參數前面
注意:
底層封裝Map結構
命名參數,依然支持參數【param1,param2,...】
示例代碼
/*** 通過員工姓名及薪資查詢員工信息【命名參數】* @return*/public List<Employee> selectEmpByNamed(@Param("lName")String lastName,@Param("salary") double salary);
? <select id="selectEmpByNamed" resultType="employee">SELECTid,last_name,email,salaryFROMtbl_employeeWHERElast_name=#{param1}ANDsalary=#{param2}</select>
源碼分析
MapperMethod對象:【命名參數底層代碼入口】
命名參數底層封裝map為ParamMap,ParamMap繼承HashMap
ParamNameResolver對象:命名參數底層實現邏輯
final Map<String, Object> param = new ParamMap<>();int i = 0;for (Map.Entry<Integer, String> entry : names.entrySet()) {param.put(entry.getValue(), args[entry.getKey()]);// add generic param names (param1, param2, ...)final String genericParamName = GENERIC_NAME_PREFIX + (i + 1);// ensure not to overwrite parameter named with @Paramif (!names.containsValue(genericParamName)) {param.put(genericParamName, args[entry.getKey()]);}i++;}return param;
?1.4 POJO參數
Mybatis支持POJO【JavaBean】入參,參數key是POJO中屬性
1.5 Map參數
Mybatis支持直接Map入參,map的key=參數key
1.6 Collection|List|Array等參數
參數名:collection、list、array
2、Mybatis參數傳遞【#與$區別】
2.1 回顧JDBC
在MyBatis和JDBC的背景下,讓我們深入了解每個組件:
1.DriverManager(驅動管理器):這是JDBC API的一部分,負責管理數據庫驅動程序的列表。當你使用DriverManager時,可以通過URL字符串獲取與特定數據庫的連接。DriverManager類動態加載JDBC驅動程序,這使得JDBC API可以通過JDBC驅動程序連接支持SQL的任何數據庫。
2.Connection(連接):連接表示與特定數據庫的會話。在MyBatis的上下文中,連接對象是從DriverManager獲取的。它提供了用于創建Statement和PreparedStatement對象的方法。Connection接口允許你與數據庫進行通信,并執行各種操作,如執行SQL語句、提交事務和管理連接屬性。
3.Statement(語句):Statement對象用于執行靜態SQL語句并返回其產生的結果。當你使用Statement時,SQL查詢通常通過串聯字符串構造,如果不正確處理,可能會導致SQL注入漏洞。這是在JDBC中執行SQL查詢的最基本方法,但由于安全方面的考慮,不建議使用。
4.PreparedStatement(預編譯語句):PreparedStatement擴展了Statement,用于執行帶參數的SQL查詢。與Statement不同,PreparedStatement中創建帶有參數占位符(通常用問號?表示)的SQL語句。這樣做可以提高性能和安全性,因為SQL語句會被數據庫預編譯和緩存,減少解析開銷,并保護免受SQL注入攻擊。
5.ResultSet(結果集):ResultSet對象表示SQL查詢的結果。它提供了用于遍歷和訪問數據庫執行查詢后返回的數據的方法。通過ResultSet,你可以遍歷數據行,檢索列值,并對結果集執行各種操作。
2.2 #與$區別
【#】底層執行SQL語句的對象,使用PreparedStatementd,預編譯SQL,防止SQL注入安全隱患,相對比較安全。
【$】底層執行SQL語句的對象使用Statement對象,未解決SQL注入安全隱患,相對不安全。
2.3 #與$使用場景
查詢SQL:select col,col2 from table1 where col=? and col2=? ?group by ?, order by ? ?limit ?,?
#使用場景,sql占位符位置均可以使用#
$使用場景,#解決不了的參數傳遞問題,均可以交給$處理【如:form 動態化表名】
/*** 測試$使用場景*/public List<Employee> selectEmpByDynamitTable(@Param("tblName") String tblName);
<select id="selectEmpByDynamitTable" resultType="employee">SELECTid,last_name,email,salaryFROM${tblName}</select>
3、Mybatis查詢中返回值四種情況
3.1 查詢單行數據返回單個對象
/*** 通過id獲取員工信息*/public Employee selectEmpById(int empId);
<select id="selectEmpById" resultType="employee">SELECTid,last_name,email,salaryFROMtbl_employeeWHEREid=#{empId}</select>
3.2 查詢多行數據返回對象的集合
/*** 查詢所有員工信息*/public List<Employee> selectAllEmps();
<select id="selectAllEmps" resultType="employee">SELECTid,last_name,email,salaryFROMtbl_employee</select>
注意:如果返回的是集合,那應該設置為**集合包含的類型**,而不是集合本身的類型。
3.3 查詢單行數據返回Map集合
Map<String key,Object value>
字段作為Map的key,查詢結果作為Map的Value
示例代碼
? /*** 查詢單行數據返回Map集合* @return*/public Map<String,Object> selectEmpReturnMap(int empId);
? <!-- ? ?查詢單行數據返回Map集合--><select id="selectEmpReturnMap" resultType="map">SELECTid,last_name,email,salaryFROMtbl_employeeWHEREid=#{empId}</select>
3.4 查詢多行數據返回Map集合
Map<Integer key,Employee value>
對象的id作為key
對象作為value
示例代碼
/*** 查詢多行數據返回Map* Map<Integer,Object>* Map<Integer,Employee>* ? ? ?對象Id作為:key* ? ? ?對象作為:value* @return*/@MapKey("id")public Map<Integer,Employee> selectEmpsReturnMap();
<select id="selectEmpsReturnMap" resultType="map">SELECTid,last_name,email,salaryFROMtbl_employee</select>
4、Mybatis中自動映射與自定義映射
自動映射【resultType】
自定義映射【resultMap】
4.1 自動映射與自定義映射
自動映射【resultType】:指的是自動將表中的字段與類中的屬性進行關聯映射
自動映射解決不了兩類問題 ?
多表連接查詢時,需要返回多張表的結果集
單表查詢時,不支持駝峰式自動映射【不想為字段定義別名】
自定義映射【resultMap】:自動映射解決不了問題,交給自定義映射
注意:resultType與resultMap只能同時使用一個
4.2 自定義映射-級聯映射
<!-- ? ?自定義映射 【員工與部門關系】--><resultMap id="empAndDeptResultMap" type="employee"><!-- ?定義主鍵字段與屬性關聯關系 --><id column="id" property="id"></id><!-- ?定義非主鍵字段與屬性關聯關系--><result column="last_name" property="lastName"></result><result column="email" property="email"></result><result column="salary" property="salary"></result><!-- ? ? ? ?為員工中所屬部門,自定義關聯關系--><result column="dept_id" property="dept.deptId"></result><result column="dept_name" property="dept.deptName"></result></resultMap><select id="selectEmpAndDeptByEmpId" resultMap="empAndDeptResultMap">SELECTe.`id`,e.`email`,e.`last_name`,e.`salary`,d.`dept_id`,d.`dept_name`FROMtbl_employee e,tbl_dept dWHEREe.`dept_id` = d.`dept_id`ANDe.`id` = #{empId}</select>
4.3 自定義映射-association映射
特點:解決一對一映射關系【多對一】
示例代碼
<!-- ? ?自定義映射 【員工與部門關系】--><resultMap id="empAndDeptResultMapAssociation" type="employee"><!-- ?定義主鍵字段與屬性關聯關系 --><id column="id" property="id"></id><!-- ?定義非主鍵字段與屬性關聯關系--><result column="last_name" property="lastName"></result><result column="email" property="email"></result><result column="salary" property="salary"></result><!-- ? ? ? ?為員工中所屬部門,自定義關聯關系--><association property="dept"javaType="com.atguigu.mybatis.pojo.Dept"><id column="dept_id" property="deptId"></id><result column="dept_name" property="deptName"></result></association></resultMap>
4.4 自定義映射-collection映射
示例代碼
/*** 通過部門id獲取部門信息,及部門所屬員工信息*/public Dept selectDeptAndEmpByDeptId(int deptId);
<resultMap id="deptAndempResultMap" type="dept"><id property="deptId" column="dept_id"></id><result property="deptName" column="dept_name"></result><collection property="empList"ofType="com.atguigu.mybatis.pojo.Employee"><id column="id" property="id"></id><result column="last_name" property="lastName"></result><result column="email" property="email"></result><result column="salary" property="salary"></result></collection></resultMap><select id="selectDeptAndEmpByDeptId" resultMap="deptAndempResultMap">SELECTe.`id`,e.`email`,e.`last_name`,e.`salary`,d.`dept_id`,d.`dept_name`FROMtbl_employee e,tbl_dept dWHEREe.`dept_id` = d.`dept_id`ANDd.dept_id = #{deptId}</select>
4.5 ResultMap相關標簽及屬性
resultMap標簽:自定義映射標簽
id屬性:定義唯一標識
type屬性:設置映射類型
resultMap子標簽
id標簽:定義主鍵字段與屬性關聯關系
result標簽:定義非主鍵字段與屬性關聯關系
column屬性:定義表中字段名稱
property屬性:定義類中屬性名稱
association標簽:定義一對一的關聯關系
property:定義關聯關系屬性
javaType:定義關聯關系屬性的類型
select:設置分步查詢SQL全路徑
colunm:設置分步查詢SQL中需要參數
fetchType:設置局部延遲加載【懶加載】是否開啟
collection標簽:定義一對多的關聯關系
property:定義一對一關聯關系屬性
ofType:定義一對一關聯關系屬性類型
fetchType:設置局部延遲加載【懶加載】是否開啟
4.6 Mybatis中分步查詢
為什么使用分步查詢【分步查詢優勢】?
將多表連接查詢,改為【分步單表查詢】,從而提高程序運行效率
示例代碼
一對一
? /*** 通過員工id獲取員工信息及員工所屬的部門信息【分步查詢】1. 先通過員工id獲取員工信息【id、last_name、email、salary、dept_id】2. 再通過部門id獲取部門信息【dept_id、dept_name】*/public Employee selectEmpAndDeptByEmpIdAssociationStep(int empId);
? ?
<select id="selectEmpAndDeptByEmpIdAssociationStep" resultMap="empAndDeptResultMapAssocationStep">selectid,last_name,email,salary,dept_idfromtbl_employeewhereid=#{empId}</select>
/*** 通過部門id獲取部門信息*/public Dept selectDeptByDeptId(int deptId);
<select id="selectDeptByDeptId" resultType="dept">selectdept_id,dept_namefromtbl_deptwheredept_id=#{deptId}</select>
一對多
? /*** 通過部門id獲取部門信息,及部門所屬員工信息【分步查詢】1. 通過部門id獲取部門信息2. 通過部門id獲取員工信息*/public Dept selectDeptAndEmpByDeptIdStep(int deptId);
<!-- ? ?通過部門id獲取部門信息,及部門所屬員工信息【分步查詢】--><!-- ? ?1. 通過部門id獲取部門信息--><!-- ? ?2. 通過部門id獲取員工信息--><select id="selectDeptAndEmpByDeptIdStep" resultMap="deptAndEmpResultMapStep">selectdept_id,dept_namefromtbl_deptwheredept_id=#{deptId}</select>
/*** 通過部門Id獲取員工信息* @param deptId* @return*/public List<Employee> selectEmpByDeptId(int deptId);
? <select id="selectEmpByDeptId" resultType="employee">selectid,last_name,email,salary,dept_idfromtbl_employeewheredept_id=#{deptId}</select>
4.7 Mybatis延遲加載【懶加載】
需要時加載,不需要暫時不加載
優勢:提升程序運行效率
語法
全局設置
<!-- 開啟延遲加載 --><setting name="lazyLoadingEnabled" value="true"/><!-- 設置加載的數據是按需加載3.4.2及以后的版本該步驟可省略--><setting name="aggressiveLazyLoading" value="false"/>
局部設置
fetchType
eager:關閉局部延遲加載
?lazy:開啟局部延遲加載
示例代碼
<association property="dept"select="com.atguigu.mybatis.mapper.DeptMapper.selectDeptByDeptId"column="dept_id"fetchType="eager"></association>
4.8 擴展
如果分步查詢時,需要傳遞給調用的查詢中多個參數,則需要將多個參數封裝成
Map來進行傳遞,語法如下**: {k1=v1, k2=v2....}
5、Mybatis動態SQL【重點】
5.1 動態SQL概述
動態SQL指的是:SQL語句可動態化
Mybatis的動態SQL中支持OGNL表達式語言,OGNL( Object Graph Navigation Language )對象圖導航語言
5.2 常用標簽
if標簽:用于完成簡單的判斷
where標簽:用于解決where關鍵字及where后第一個and或or的問題
trim標簽: 可以在條件判斷完的SQL語句前后添加或者去掉指定的字符
prefix: 添加前綴
prefixOverrides: 去掉前綴
suffix: 添加后綴
suffixOverrides: 去掉后綴
set標簽:主要用于解決set關鍵字及多出一個【,】問題
choose標簽:類似java中if-else【switch-case】結構
foreach標簽:類似java中for循環
collection: 要迭代的集合
item: 當前從集合中迭代出的元素
separator: 元素與元素之間的分隔符
open: 開始字符
close:結束字符
sql標簽:提取可重用SQL片段
5.3 示例代碼
<?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.atguigu.mybatis.mapper.EmployeeMapper"><sql id="emp_col">id,last_name,email,salary</sql><sql id="select_employee">selectid,last_name,email,salaryfromtbl_employee</sql><!-- 按條件查詢員工信息【條件不確定】--><select id="selectEmpByOpr" resultType="employee"><include refid="select_employee"></include><where><if test="id != null">and id = #{id}</if><if test="lastName != null">and last_name = #{lastName}</if><if test="email != null">and email = #{email}</if><if test="salary != null">and salary = #{salary}</if></where></select><select id="selectEmpByOprTrim" resultType="employee"><include refid="select_employee"></include><trim prefix="where" suffixOverrides="and"><if test="id != null">id = #{id} and</if><if test="lastName != null">last_name = #{lastName} and</if><if test="email != null">email = #{email} and</if><if test="salary != null">salary = #{salary}</if></trim></select><update id="updateEmpByOpr">updatetbl_employee<set><if test="lastName != null">last_name=#{lastName},</if><if test="email != null">email=#{email},</if><if test="salary != null">salary=#{salary}</if></set>whereid = #{id}</update><select id="selectEmpByOneOpr" resultType="employee">select<include refid="emp_col"></include>fromtbl_employee<where><choose><when test="id != null">id = #{id}</when><when test="lastName != null">last_name = #{lastName}</when><when test="email != null">email = #{email}</when><when test="salary != null">salary = #{salary}</when><otherwise>1=1</otherwise></choose></where></select><select id="selectEmpByIds" resultType="employee">selectid,last_name,email,salaryfromtbl_employee<where>id in(<foreach collection="ids" item="id" separator=",">#{id}</foreach>)</where></select><insert id="batchInsertEmp">INSERT INTOtbl_employee(last_name,email,salary)VALUES<foreach collection="employees" item="emp" separator=",">(#{emp.lastName},#{emp.email},#{emp.salary})</foreach></insert></mapper>
6、 Mybatis中緩存機制
6.1 緩存概述
生活中緩存
緩存一些音頻、視頻優勢
節約數據流量
提高播放性能
程序中緩存【Mybatis緩存】
使用緩存優勢
提高查詢效率
降低服務器壓力
6.2 Mybatis中的緩存概述
一級緩存
二級緩存
第三方緩存
6.3 Mybatis緩存機制之一級緩存
概述:一級緩存【本地緩存(Local Cache)或SqlSession級別緩存】
特點
一級緩存默認開啟
不能關閉
可以清空
緩存原理
?第一次獲取數據時,先從數據庫中加載數據,將數據緩存至Mybatis一級緩存中【緩存底層實現原理Map,key:hashCode+查詢的SqlId+編寫的sql查詢語句+參數】
以后再次獲取數據時,先從一級緩存中獲取,**如未獲取到數據**,再從數據庫中獲取數據。
一級緩存五種失效情況
? 1) 不同的SqlSession對應不同的一級緩存
? 2) 同一個SqlSession但是查詢條件不同
? 3) 同一個SqlSession兩次查詢期間執行了任何一次增刪改操作
? 4) 同一個SqlSession兩次查詢期間手動清空了緩存
sqlSession.clearCache()
? 5) 同一個SqlSession兩次查詢期間提交了事務
sqlSession.commit()
6.4 Mybatis緩存機制之二級緩存
二級緩存【second level cache】概述
二級緩存【全局作用域緩存】
?SqlSessionFactory級別緩存
二級緩存特點
二級緩存默認關閉,需要開啟才能使用
二級緩存需要提交sqlSession或關閉sqlSession時,才會緩存。
二級緩存使用的步驟:
? ① 全局配置文件中開啟二級緩存<setting name="cacheEnabled" value="true"/>
? ② 需要使用二級緩存的**映射文件處**使用cache配置緩存<cache />
? ③ 注意:POJO需要實現Serializable接口
?
④ 關閉sqlSession或提交sqlSession時,將數據緩存到二級緩存
二級緩存底層原理
第一次獲取數據時,先從數據庫中獲取數據,將數據緩存至一級緩存;當提交或關閉SqlSession時,將數據緩存至二級緩存
以后再次獲取數據時,先從一級緩存中獲取數據,如一級緩存沒有指定數據,再去二級緩存中獲取數據。如二級緩存也沒有指定數據時,需要去數據庫中獲取數據,......
二級緩存相關屬性
eviction=“FIFO”:緩存清除【回收】策略。
LRU – 最近最少使用的:移除最長時間不被使用的對象。
FIFO – 先進先出:按對象進入緩存的順序來移除它們。
flushInterval:刷新間隔,單位毫秒
size:引用數目,正整數
readOnly:只讀,true/false
二級緩存的失效情況
在兩次查詢之間,執行增刪改操作,會同時清空一級緩存和二級緩存
sqlSession.clearCache():只是用來清除一級緩存。
6.5 Mybatis中緩存機制之第三方緩存
第三方緩存:EhCache
EhCache 是一個純Java的進程內緩存框架
使用步驟
導入jar包
<!-- mybatis-ehcache --><dependency><groupId>org.mybatis.caches</groupId><artifactId>mybatis-ehcache</artifactId><version>1.0.3</version></dependency><!-- slf4j-log4j12 --><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>1.6.2</version><scope>test</scope></dependency>
編寫配置文件【ehcache.xml】
<?xml version="1.0" encoding="UTF-8"?><ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:noNamespaceSchemaLocation="../config/ehcache.xsd"><!-- 磁盤保存路徑 --><diskStore path="E:\mybatis\ehcache" /><defaultCachemaxElementsInMemory="512"maxElementsOnDisk="10000000"eternal="false"overflowToDisk="true"timeToIdleSeconds="120"timeToLiveSeconds="120"diskExpiryThreadIntervalSeconds="120"memoryStoreEvictionPolicy="LRU"></defaultCache></ehcache>
加載第三方緩存【映射文件】
開始使用
注意事項
第三方緩存,需要建立在二級緩存基礎上【需要開啟二級緩存,第三方緩存才能生效】
如何讓第三方緩存失效【將二級緩存設置失效即可】
?