這里寫目錄標題
- 一、MyBatis的一級緩存
- 1.1、工作原理
- 1.2、一級緩存失效的四種情況
- 1.3、不同的SqlSession對應不同的一級緩存
- 1.4、同一個SqlSession但是查詢條件不同
- 1.5、同一個SqlSession兩次查詢期間執行了任何一次增刪改操作
- 1.6、同一個SqlSession兩次查詢期間手動清空了(一級)緩存
- 二、MyBatis的二級緩存
- 2.1、二級緩存開啟的條件
- 2.2、測試
- 2.3、二級緩存的相關配置
- (1)eviction屬性:緩存回收策略
- (2)flushInterval屬性:刷新間隔,單位毫秒
- (3)size屬性:引用數目,正整數
- (4)readOnly屬性:只讀,true/false
- 三、整合第三方緩存EHCache
- 3.1、pom.xml文件中添加依賴:
- 3.2、創建EHCache的配置文件ehcache.xml
- 3.3、設置二級緩存類型
- 3.4、加入logback日志
數據庫查詢是應用性能的常見瓶頸(磁盤IO比內存IO慢10^6倍以上),緩存通過將頻繁查詢的結果存儲在內存中,避免重復訪問數據庫。
MyBatis提供兩級緩存,工作流如下:
- 一級緩存(SqlSession級別):默認開啟,緩存當前會話的查詢結構;
- 二級緩存(Mapper級別):需手動開啟,緩存Mapper接口的查詢結果,可以被多個SqlSession共享。
查詢數據時,MyBatis的緩存查詢順序:
二級緩存——>一級緩存——>數據庫
即先查詢二級緩存,若為命中則查詢一級緩存,仍為命中才查詢數據庫。
一、MyBatis的一級緩存
一級緩存是SqlSession級別的,通過同一個SqlSession查詢的數據會被緩存,下次查詢相同的數據就會從緩存中直接獲取,不會從數據庫重新訪問。整個查詢是綁定到SqlSession(會話),生命周期與SqlSession一致。
1.1、工作原理
- 緩存范圍 :每個SqlSession擁有獨立的一級緩存,不同SqlSession的緩存互不影響;
- 緩存時機 :SqlSession執行select查詢后,會將結果存入一級緩存;
- 命中條件 :相同的Mapper方法+相同的參數+相同的SQL;
- 失效場景 :SqlSession執行insert/update/delete(會清空當前SqlSession的一級緩存)、SqlSession關閉或提交。
1.2、一級緩存失效的四種情況
- 不同的SqlSession對應不同的一級緩存
- 同一個SqlSession但是查詢條件不同
- 同一個SqlSession兩次查詢期間執行了任何一次增刪改操作
- 同一個SqlSession兩次查詢期間手動清空了緩存
1.3、不同的SqlSession對應不同的一級緩存
CacheMapper.java文件:
Emp getEmpById(Integer eid);
CacheMappe.xml文件:
<select id="getEmpById" resultType="Emp">select * from t_emp where 1=1<if test="eid != null and eid != ''">and eid = #{eid}</if></select>
測試文件:
@Testpublic void testCache() throws IOException {SqlSession sqlSession = SqlSessionUtils.getSqlSession();CacheMapper mapper = sqlSession.getMapper(CacheMapper.class);System.out.println("第一次查詢(未命中緩存,查詢數據庫)");Emp emp1 = mapper.getEmpById(13);System.out.println(emp1);System.out.println("\n第二次查詢(相同SqlSession+相同參數,命中一級緩存,不查數據庫");Emp emp2 = mapper.getEmpById(13);System.out.println(emp2);System.out.println("\n即使用的不是同一個Mapper,也同樣從緩存中取(同一個sqlsession)");CacheMapper mapper2 = sqlSession.getMapper(CacheMapper.class);Emp empByMapper2 = mapper2.getEmpById(13);System.out.println(empByMapper2);System.out.println("\n一級緩存的范圍在sqlsession中,換一個新的sqlsession就會再次用sql讀取數據");SqlSession sqlSession2 = SqlSessionUtils.getSqlSession();CacheMapper mapper2BySqlSession2 = sqlSession2.getMapper(CacheMapper.class);System.out.println(mapper2BySqlSession2.getEmpById(13));}
測試結果:
1.4、同一個SqlSession但是查詢條件不同
@Testpublic void testCache2() throws IOException {SqlSession sqlSession = SqlSessionUtils.getSqlSession();CacheMapper mapper = sqlSession.getMapper(CacheMapper.class);System.out.println("=====第一次獲取數據=====");Emp emp1 = mapper.getEmpById(13);System.out.println(emp1);System.out.println("\n=====查詢條件不同=====");Emp emp2 = mapper.getEmpById(12);System.out.println(emp2);}
1.5、同一個SqlSession兩次查詢期間執行了任何一次增刪改操作
@Testpublic void testCache3() throws IOException {SqlSession sqlSession = SqlSessionUtils.getSqlSession();CacheMapper mapper = sqlSession.getMapper(CacheMapper.class);System.out.println("=====第一次獲取數據=====");Emp emp1 = mapper.getEmpById(13);System.out.println(emp1);Emp emp2 = mapper.getEmpById(13);System.out.println(emp2);System.out.println("\n=====進行增刪改操作=====");mapper.insetEmp(new Emp(null, "Joey", 44, "男", "8888@gmai.com"));System.out.println("\n=====同一個sqlsession,再獲取數據=====");Emp emp3 = mapper.getEmpById(13);System.out.println(emp3);}
1.6、同一個SqlSession兩次查詢期間手動清空了(一級)緩存
@Testpublic void testCache4() throws IOException {SqlSession sqlSession = SqlSessionUtils.getSqlSession();CacheMapper mapper = sqlSession.getMapper(CacheMapper.class);System.out.println("=====第一次獲取數據=====");Emp emp1 = mapper.getEmpById(13);System.out.println(emp1);System.out.println("\n=====兩次查詢期間手動清空緩存=====");sqlSession.clearCache();System.out.println("\n=====再次查詢id=3的emp=====");Emp emp2 = mapper.getEmpById(13);System.out.println(emp2);}
二、MyBatis的二級緩存
二級緩存是跨SqlSession的全局緩存,綁定到Mapper接口(同一個Mapper的所有方法共享),需手動開啟。
二級緩存是SqlSessionFactory級別,通過同一個SqlSessionFactory創建的SqlSession查詢的結果被緩存,此后若再次執行相同的查詢語句,結構就會從緩存中獲取。
注意:使二級緩存失效的情況:兩次查詢之間執行了任意的增刪改,會使一級和二級緩存同時失效,沒有提交sqlsession時,贖回包保存在一級緩存中,提交后,會保存在二級緩存中。
2.1、二級緩存開啟的條件
- 在核心配置文件中,設置全局配置屬性(在settings里面設置)
<setting name="cacheEnabled" value="true"/>
- 在隱射文件中設置標簽
- 二級緩存必循在SqlSession關閉活提交之后有效
- 查詢的數據所轉換的實體類類型必須實現序列化的接口
2.2、測試
@Testpublic void testCacheTwo(){//這里不能用工具類了,因為每次都會創建新的sqlsessionfactory//SqlSession sqlSession = SqlSessionUtils.getSqlSession();//CacheMapper mapper = sqlSession.getMapper(CacheMapper.class);//只要是同一個sqlsessionfactory獲得的sqlsession就可以try {InputStream is = Resources.getResourceAsStream("mybatis-config.xml");SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);SqlSession sqlSession1 = sqlSessionFactory.openSession(true);CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class);System.out.println(mapper1.getEmpById(13));//打開二級緩存未打開sqlSession1.close();System.out.println("Cache Hit Ratio:緩存命中率,指的是在緩存中有沒有這條數據");System.out.println("=====二級緩打開,從緩存中獲取數據=====");SqlSession sqlSession2 = sqlSessionFactory.openSession(true);CacheMapper mapper2 = sqlSession2.getMapper(CacheMapper.class);System.out.println(mapper2.getEmpById(13));} catch (IOException e) {e.printStackTrace();}}
2.3、二級緩存的相關配置
在mapper配置文件中添加的cache標簽可以設置一些屬性:
(1)eviction屬性:緩存回收策略
LRU(Least Recently Used) – 最近最少使用的:移除最長時間不被使用的對象。
FIFO(First in First out) – 先進先出:按對象進入緩存的順序來移除它們。
SOFT – 軟引用:移除基于垃圾回收器狀態和軟引用規則的對象。
WEAK –弱引用:更積極地移除基于垃圾收集器狀態和弱引用規則的對象。
默認的是 LRU。
(2)flushInterval屬性:刷新間隔,單位毫秒
默認情況是不設置,也就是沒有刷新間隔,緩存僅僅調用語句(增刪改) 時刷新
(3)size屬性:引用數目,正整數
代表緩存最多可以存儲多少個對象,太大容易導致內存溢出
(4)readOnly屬性:只讀,true/false
- true:只讀緩存; 會給所有調用者返回緩存對象的相同實例。因此這些對象不能被修改。這提供了很重要的性能優勢。【性能好】
- false:讀寫緩存; 會返回緩存對象的拷貝(通過序列化)。這會慢一些,但是安全,因此默認是 false。【安全】
三、整合第三方緩存EHCache
EHCache只能替代二級緩存,所以只要了解會配置即可。
3.1、pom.xml文件中添加依賴:
<!-- Mybatis EHCache整合包 -->
<dependency><groupId>org.mybatis.caches</groupId><artifactId>mybatis-ehcache</artifactId><version>1.2.1</version>
</dependency><!-- slf4j日志門面的一個具體實現 -->
<dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.3</version>
</dependency>
各種jar包的功能
3.2、創建EHCache的配置文件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="這里改成你需要保存緩存的磁盤路徑"/><defaultCachemaxElementsInMemory="1000"maxElementsOnDisk="10000000"eternal="false"overflowToDisk="true"timeToIdleSeconds="120"timeToLiveSeconds="120"diskExpiryThreadIntervalSeconds="120"memoryStoreEvictionPolicy="LRU"></defaultCache>
</ehcache>
"…/config/ehcache.xsd”報錯,則創建這個文件
3.3、設置二級緩存類型
在相對應的xxxMapper.xml中,加入:
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
3.4、加入logback日志
存在SLF4J時,作為簡易日志的log4j將失效,此時我們需要借助SLF4J的具體實現logback來打印日志。
創建logback的配置文件logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true"> <!-- 指定日志輸出的位置 --><appender name="STDOUT">class="ch.qos.logback.core.ConsoleAppender"><encoder><!-- 日志輸出的格式 --><!-- 按照順序分別是:時間、日志級別、線程名稱、打印日志的類、日志主體內容、換行 --><pattern>[%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern></encoder></appender><!-- 設置全局日志級別。日志級別按順序分別是:DEBUG、INFO、WARN、ERROR --><!-- 指定任何一個日志級別都只打印當前級別和后面級別的日志。 --><root level="DEBUG"><!-- 指定打印日志的appender,這里通過“STDOUT”引用了前面配置的appender --><appender-ref ref="STDOUT" /></root><!-- 根據特殊需求指定局部日志級別 --><logger name="com.atguigu.mybatis.mapper" level="DEBUG"/>
</configuration>