MyBatis的緩存解析
MyBatis 的緩存
1. MyBatis 的一級緩存
一級緩存是 SqlSession 級別的,通過同一個 SqlSession 查詢的數(shù)據(jù)會緩存,下次查詢相同的數(shù)據(jù)就會從緩存中直接獲取,不會從數(shù)據(jù)重新訪問,前提必須是同一個 SqlSession 對象,并且查詢的數(shù)據(jù)相同
注意是: SqlSession 是只針對 查詢的 ,并且一級 SqlSession 是默認開啟的
使用一級緩存失效的四種情況
- 查詢使用的 SqlSession 對象不同,就是不同 SqlSession 對應(yīng)不同的一級緩存
- 同一個 SqlSession 但是查詢條件不同,就是獲取到數(shù)據(jù)不同
- 同一個 SqlSession 兩次查詢期間執(zhí)行了任何一次增刪改操作**【就是在兩次查詢期間執(zhí)行了任何一次增刪改操作, MyBatis 就是將一級緩存,清空掉】**
- 同一個 SqlSession 再次查詢期間手動清空緩存 【注意是只會清空一級緩存】
調(diào)用`SqlSession`對象中的`clearCache()`方法,手動清空緩存
案例:
Mapper 接口
// 根據(jù)ID查詢員工信息,驗證一級緩存 Emp getEmpByID(@Param("eid") Integer eid); // 插入數(shù)據(jù),驗證任何的增刪改都會使用`MyBatis`清空一級緩存 int insertEmp(Emp emp);
Mapper.xml 文件
<!--查詢員工信息--> <select id="getEmpByID" resultType="Emp"> select * from emp where eid=#{eid}; </select> <!-- 插入數(shù)據(jù),驗證任何的增刪改都會使用`MyBatis`清空一級緩存 insertEmp(Emp emp); --> <insert id="insertEmp" > insert into emp values (null,#{empName},#{age},#{sex},#{email},null); </insert>
test 類
@Test public void testGetEmpById(){ SqlSession sqlSession = SqlSessionUtils.getSqlSession(); CacheMapper mapper = sqlSession.getMapper(CacheMapper.class); Emp emp1 = mapper.getEmpByID(1); Emp emp2 = mapper.getEmpByID(1); // 兩次查詢只輸出一條sql:select * from emp where eid=?; 說明從一級緩存獲取數(shù)據(jù) System.out.println(emp1); System.out.println(emp2); } @Test public void testInsetEmp(){ SqlSession sqlSession = SqlSessionUtils.getSqlSession(); CacheMapper mapper = sqlSession.getMapper(CacheMapper.class); Emp emp1 = mapper.getEmpByID(1); // 說明任何的增刪改都會使用`MyBatis`清空一級緩存 int i = mapper.insertEmp(new Emp(null, "a", 20, "男", "123qq.com")); // 手動清空`mybatis`一級緩存 ,也可以使用兩次查詢相同數(shù)據(jù)時,訪問兩次數(shù)據(jù)庫 // sqlSession.clearCache(); Emp emp2 = mapper.getEmpByID(1); System.out.println(emp1); System.out.println(emp2); System.out.println(i); }
2.二級緩存
注意是:當(dāng)我們沒有關(guān)閉 SqlSession 或沒有提交事務(wù)時,數(shù)據(jù)是保存在 一級緩存 中,當(dāng)我們關(guān)閉 SqlSession 或提交事務(wù)后,數(shù)據(jù)是保存在 二級緩存 中
sqlSession.commit(); sqlSession.close(); 數(shù)據(jù)會保存到二級緩存 ,必須我們手動提交事務(wù)或關(guān)閉流才能使數(shù)據(jù)保存到二級緩存中,否則只會保存在一級緩存中 sqlSession1.commit(); //sqlSession1.commit();或sqlSession1.close();都會使用數(shù)據(jù)保存到二級緩存中,否則只會保存在一級緩存中
概念:
二級緩存是 SqlSessionFactory 級別,通過同一個 SqlSessionFactory 創(chuàng)建的 SqlSession 查詢到的結(jié)果會被緩存,此后若再次執(zhí)行相同的查詢語句,結(jié)果就會從緩存中獲取
1.二級緩存開啟條件:
- 在核心配置文件中,設(shè)置全局配置屬性 cachEnabled=true ,默認為 true ,不需要設(shè)置
- 在映射文件中設(shè)置 <cache/> 【就是在 Mapper.xml 文件中設(shè)置】
<!--開啟二級緩存--> <cache />
- 二級緩存必須在 SqlSession 關(guān)閉或提交之后有效
sqlSession.commit(); sqlSession.close(); 數(shù)據(jù)會保存到二級緩存 ,必須我們手動提交事務(wù)或關(guān)閉流才能使數(shù)據(jù)保存到二級緩存中,否則只會保存在一級緩存中 sqlSession1.commit(); //sqlSession1.commit();或sqlSession1.close();都會使用數(shù)據(jù)保存到二級緩存中,否則只會保存在一級緩存中
- 查詢的數(shù)據(jù)所轉(zhuǎn)換的實體類類型必須實現(xiàn) 序列化接口 【就是查詢的數(shù)據(jù)封裝成 JavaBean 類該類必須實現(xiàn) 序列化接口 】
public class Emp implements Serializable {代碼}
2.二級緩存失效的情況:【只有一種情況就是 增刪改 】
兩次查詢之間執(zhí)行了任意的 增刪改 ,都會使 一級 和 二級 緩存 同時失效
注意是:手動調(diào)用SqlSession 對象中的 clearCache()`方法,手動清空的緩存 ,只會清空一級緩存,對二級緩存不起作用,只有增刪改對二級緩存失效
3.二級緩存相關(guān)配置
在 mapper 配置文件中添加的 cache 標簽可以設(shè)置一些屬性:
<cache eviction="" blocking="" flushInterval="" readOnly="" size="" type="" /> type:該屬性是來設(shè)置使用第三方整合二級緩存,如果不指定默認使用的是`MyBatis`中的二級緩存
- eviction屬性:緩存回收策略 【mybatis采用是這種方式】
- LRU(Least Recently Used) – 最近最少使用的:移除最長時間不被使用的對象。
- FIFO(First in First out) – 先進先出:按對象進入緩存的順序來移除它們。
- SOFT – 軟引用:移除基于垃圾回收器狀態(tài)和軟引用規(guī)則的對象。
- WEAK – 弱引用:更積極地移除基于垃圾收集器狀態(tài)和弱引用規(guī)則的對象。 默認的是 LRU。
- flushInterval屬性:刷新間隔,單位毫秒
- 默認情況是不設(shè)置,也就是沒有刷新間隔,緩存僅僅調(diào)用語句時刷新
- size屬性:引用數(shù)目,正整數(shù)
- 代表緩存最多可以存儲多少個對象,太大容易導(dǎo)致內(nèi)存溢出
- readOnly屬性:只讀,true/false
- true:只讀緩存;會給所有調(diào)用者返回緩存對象的相同實例。因此這些對象不能被修改。這提供了 很重要的性能優(yōu)勢。
- false:讀寫緩存;會返回緩存對象的拷貝(通過序列化)。這會慢一些,但是安全,因此默認是 false。
3.MyBatis緩存查詢的順序
- 先查詢二級緩存,因為二級緩存中可能會有其他程序已經(jīng)查出來的數(shù)據(jù),可以拿來直接使用
- 如果二級緩存沒有命中,再查詢一級緩存
- 如果一級緩存也沒有命中,則查詢數(shù)據(jù)庫
- SqlSession關(guān)閉或提交后,一級緩存中的數(shù)據(jù)會寫入二級緩存中
4.整合第三方緩存EHCache
注意是:整合第三方緩存,只能對 MyBatis 二級緩存作用,對一級緩存不起作用,整合第三緩存,只是替換 mybatis 中的二級緩存的
使用步驟:
1.添加依賴
<!-- Mybatis EHCache整合包 --> <dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-ehcache</artifactId> <version>1.2.1</version> </dependency> <!-- slf4j日志門面的一個具體實現(xiàn) --> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency>
2.各個 jar 包功能
jar 包名稱 | 作用 |
mybatis-ehcache | Mybatis和EHCache整合包 |
ehcache | EHCache核心包 |
slf4j-api | SLF4J日志面包【就是接口】 |
logback-classic | 支持SLF4J門面接口一個具體實現(xiàn)【就是接口的實現(xiàn)類】 |
3. 創(chuàng)建EHCache的配置文件ehcache.xml 【注意是:名稱必須是 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="D:\haikang\ehcache"/> <defaultCache maxElementsInMemory="1000" maxElementsOnDisk="10000000" eternal="false" overflowToDisk="true" timeToIdleSeconds="120" timeToLiveSeconds="120" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> </defaultCache> </ehcache>
4.設(shè)置二級緩存的類型【在 Mapper.xml 文件中設(shè)置】
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
5.加入 logback 日志【日志名稱不能修改】
存在 SLF4J 時,作為簡易日志的 log4j 將失效,此時我們需要借助 SLF4J 的具體實現(xiàn) logback 來打印日志。 創(chuàng)建 logback 的配置文件 logback.xml
<?xml version="1.0" encoding="UTF-8"?> <configuration debug="true"> <!-- 指定日志輸出的位置 --> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <!-- 日志輸出的格式 --> <!-- 按照順序分別是:時間、日志級別、線程名稱、打印日志的類、日志主體內(nèi)容、換行 --> <pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern> </encoder> </appender> <!-- 設(shè)置全局日志級別。日志級別按順序分別是:DEBUG、INFO、WARN、ERROR --> <!-- 指定任何一個日志級別都只打印當(dāng)前級別和后面級別的日志。 --> <root level="DEBUG"> <!-- 指定打印日志的appender,這里通過“STDOUT”引用了前面配置的appender --> <appender-ref ref="STDOUT" /> </root> <!-- 根據(jù)特殊需求指定局部日志級別 --> <logger name="com.atguigu.crowd.mapper" level="DEBUG"/> </configuration>
6. EHCache 配置文件說明
屬性名 | 是否必須 | 作用 |
maxElementsInMemory | 是 | 在內(nèi)存中緩存的element的最大數(shù)目 |
maxElementsOnDisk | 是 | 在磁盤上緩存的element的最大數(shù)目,若是0表示無 窮大 |
eternal | 是 | 設(shè)定緩存的elements是否永遠不過期。 如果為 true,則緩存的數(shù)據(jù)始終有效, 如果為false那么還 要根據(jù)timeToIdleSeconds、timeToLiveSeconds 判斷 |
overflowToDisk | 是 | 設(shè)定當(dāng)內(nèi)存緩存溢出的時候是否將過期的element 緩存到磁盤上 |
timeToIdleSeconds | 否 | 當(dāng)緩存在EhCache中的數(shù)據(jù)前后兩次訪問的時間超 過timeToIdleSeconds的屬性取值時, 這些數(shù)據(jù)便 會刪除,默認值是0,也就是可閑置時間無窮大 |
timeToLiveSeconds | 否 | 緩存element的有效生命期,默認是0.,也就是 element存活時間無窮大 |
diskSpoolBufferSizeMB | 否 | DiskStore(磁盤緩存)的緩存區(qū)大小。默認是 30MB。每個Cache都應(yīng)該有自己的一個緩沖區(qū) |
diskPersistent | 否 | 在VM重啟的時候是否啟用磁盤保存EhCache中的數(shù) 據(jù),默認是false。 |
diskExpiryThreadIntervalSeconds | 否 | 磁盤緩存的清理線程運行間隔,默認是120秒。每 個120s, 相應(yīng)的線程會進行一次EhCache中數(shù)據(jù)的 清理工作 |
memoryStoreEvictionPolicy | 否 | 當(dāng)內(nèi)存緩存達到最大,有新的element加入的時 候, 移除緩存中element的策略。 默認是LRU(最 近最少使用),可選的有LFU(最不常使用)和 FIFO(先進先出) |
注意文件的存放位置
到此這篇關(guān)于MyBatis的緩存解析的文章就介紹到這了,更多相關(guān)MyBatis的緩存內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用Idea簡單快速搭建springcloud項目的圖文教程
這篇文章主要介紹了使用Idea簡單快速搭建springcloud項目,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-01-01