mybatis緩存機制
簡介
MyBatis 包含一個非常強大的查詢緩存特性,
它可以非常友善地配置和定制。緩存可以極大的提升查詢效率。
MyBatis系統中預設定義了兩級緩存。
一級例子
= openSession.getMapper(EmpMapper.class);
Emp empById = mapper.getEmpById(3);
System.out.println(empById);
Emp empById2 = mapper.getEmpById(3);
System.out.println(empById==empById2);
結果是:
隻發了一條sql語句,兩個emp的記憶體位址是一樣的
DEBUG 05-19 11:55:59,176 ==> Preparing: select eid id,ename name,email,gender from emp where eid= ? (BaseJdbcLogger.java:145)
DEBUG 05-19 11:55:59,199 ==> Parameters: 3(Integer) (BaseJdbcLogger.java:145)
DEBUG 05-19 11:55:59,220 <== Total: 1 (BaseJdbcLogger.java:145)
Emp [eid=3, ename=鳳, email=jane.com, gender=女, dept=null]
true
一級,二級緩存機制
/*
* mybatis的緩存機制
* 分為兩級緩存:
* 一級緩存:(本地緩存):sqlSession級别的緩存,sqlsession級别的一個Map
* 一級緩存是一直開啟的,我們關閉不了
* 與資料庫同一次會話期間查詢到的資料會放在本地緩存中,
* 以後如果需要擷取相同的資料,直接從緩存中拿,
* 沒有必要再去查詢資料庫
*
* 一級緩存失效的情況:
* 就是沒有使用到目前一級緩存的情況,效果就是查詢相同的資料還需要向資料庫發出sql語句
* 1.sqlsession不同了
* 2.sqlsession相同,查詢條件不同了,因為目前一級緩存裡面沒有這個資料
* 3.sqlsession相同,兩次查詢之間執行了增删改操作,因為這個增删改的操作可能對目前的資料有影響
* 4.sqlsession相同,手動清除了一級緩存,調用了openSession.clearCache();
*
* 二級緩存:(全局緩存)
* 這是基于namespace級别的緩存,一個namespace就對應一個二級緩存
* 機制原理:
* 1.一個會話,查詢一條資料,這個資料就會被放在目前會話的一級緩存中
* 2.如果會話關閉,一級緩存中的資料就會被儲存到二級緩存中
* 新的會話查詢資料,就可以參照二級緩存的内容
* 3.sqlsession會話在EmpMapper中查詢了Emp資訊
* 在DeptMapper中查詢了Dept資訊
* 這兩個不同的緩存資訊是不能互相使用的
* 不同的namespace查出的資料會放在自己對應的緩存中(就是map)
* 效果:資料會從二級緩存中擷取
* 注意:查出的資料會預設先放在一級緩存中的,
* 隻有會話送出或者關閉後,一級緩存中的資料才會轉移到二級緩存中
* 是以測試的時候,兩個不同的會話調用同一個資料想使用緩存
* 必須在第二個會話調用前關閉第一個會話
* 如何使用:
* 1.開啟全局二級緩存配置,其實預設是開啟的,
* 但是我們預設是将自己需要的設定都寫上
* <setting name="cacheEnabled" value="true"/>
* 2.哪一個映射檔案需要使用二級緩存
* 在映射檔案加上<cache></cache>标簽
* 3.因為緩存使用到序列化技術,每個POJO都要實作序列化接口
*
* 使用緩存的一些小設定:
* 1.cacheEnabled=true
* false:關閉二級緩存,一級緩存是一直可以使用的
* 2.每一個select标簽都有useCache="true";的設定
* false:就是不使用二級緩存,一級緩存還是可以使用的
* 3.每一個增删改标簽都有預設的flushCache="true":(一級緩存,二級緩存都會被清除)
* 這個屬性讓增删改執行完成後清除緩存
* flushCache="true":一級緩存,二級緩存都會被清除)
* 而查詢标簽預設的flushCache="false",
* 而查詢标簽将flushCache="true",那麼每次查詢後就會清空緩存,緩存時沒有被使用的
* 4.手動調用的sqlSession.clearCache(),隻是清除目前的一級緩存,二級不會清除
* 5.全局設定還有這樣一個參數:localCacheScope:本地緩存的作用域
* 有兩個取值:
* SESSION:一級緩存,目前會話的所有資料儲存在會話緩存中;
* STATEMENT:沒有一級緩存了,相當于禁用了一級緩存
*/
@org.junit.Test
public void test9() throws IOException
{
String resource = "mybatis.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
try
{
SqlSession openSession1 = sqlSessionFactory.openSession();
SqlSession openSession2 = sqlSessionFactory.openSession();
EmpMapper mapper1 = openSession1.getMapper(EmpMapper.class);
EmpMapper mapper2 = openSession2.getMapper(EmpMapper.class);
Emp empById = mapper1.getEmpById(7);
System.out.println(empById);
openSession1.close();
Emp empById2 = mapper2.getEmpById(7);
System.out.println(empById2);
openSession2.close();
}finally
{
}
}
<cache eviction="FIFO" flushInterval="10000" readOnly="false" size="1024" ></cache>
<!--
eviction:緩存回收政策:
LRU – 最近最少使用的:移除最長時間不被使用的對象。
FIFO – 先進先出:按對象進入緩存的順序來移除它們。
SOFT – 軟引用:移除基于垃圾回收器狀态和軟引用規則的對象。
WEAK – 弱引用:更積極地移除基于垃圾收集器狀态和弱引用規則的對象。
預設的是 LRU。
flushInterval:緩存重新整理間隔
緩存多長時間清空一次,預設不清空,設定一個毫秒值
readOnly:是否隻讀,取值是布爾值
true:隻讀:mybatis認為所有從緩存中擷取資料的操作都是隻讀的,不會修改資料
mybatis為了加快擷取速度,直接将資料在緩存中的引用交給使用者
這樣速度是快了,但是這樣不安全
false:非隻讀,mybatis覺得擷取的資料可能被修改
是以就先會利用序列化和反序列化的技術克隆一份新的資料給你
這樣安全,但是速度慢
size:緩存存放多少個元素
type:指定自定義的全類名,自定義的類需要實作Cache接口
-->
POJO
public class Emp implements Serializable
{
/**
*
*/
private static final long serialVersionUID = 1L;
private Integer eid;
private String ename;
private String email;
private String gender;
private Dept dept;
結果:
可以發現,第二次拿到的結果不是發sql得到的,而是
DEBUG 05-20 12:51:52,960 Cache Hit Ratio [bean.EmpMapper]: 0.5 (LoggingCache.java:62)
直接從二級緩存裡面擷取,擷取了兩次,第二次擷取成功,是以後面的數字是1/2=0.5
DEBUG 05-20 12:51:52,791 Cache Hit Ratio [bean.EmpMapper]: 0.0 (LoggingCache.java:62)
DEBUG 05-20 12:51:52,805 ==> Preparing: select eid id,ename name,email,gender from emp where eid= ? (BaseJdbcLogger.java:145)
DEBUG 05-20 12:51:52,871 ==> Parameters: 7(Integer) (BaseJdbcLogger.java:145)
DEBUG 05-20 12:51:52,940 <== Total: 1 (BaseJdbcLogger.java:145)
Emp [eid=7, ename=jane, email=jane.com, gender=男, dept=null]
DEBUG 05-20 12:51:52,960 Cache Hit Ratio [bean.EmpMapper]: 0.5 (LoggingCache.java:62)
Emp [eid=7, ename=jane, email=jane.com, gender=男, dept=null]
緩存原理
原理示意圖
實作
執行順序
緩存的順序:二級緩存----->一級緩存------>資料庫
mybatis整合ehcache
首先需要導入三個包
ehcache需要的jar包
ehcache-core-2.6.8.jar
ehcache允許需要的日志包
slf4j-api-1.6.1.jar
slf4j-log4j12-1.6.2.jar
有着三個包後我們就可以寫自己的實作類,實作Cache接口就行
但是自己寫實作類好麻煩呀,它是一個成熟的項目,應該自己寫實作的
是以mybatis就幫你寫好了,官網的git那裡有,下載下傳好後導入相應的jar包
mybatis-ehcache-1.0.3.jar
根據官方文檔說明,它需要在映射檔案配置好,還有需要一個ehcache.xml檔案就可以使用ehcache了
EmpMapper映射檔案配置
<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
<?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:\new Java\STSworkspace\TempData\ehcache" />
<defaultCache
maxElementsInMemory="1000"
maxElementsOnDisk="10000000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
</defaultCache>
</ehcache>
<!--
屬性說明:
l diskStore:指定資料在磁盤中的存儲位置。
l defaultCache:當借助CacheManager.add("demoCache")建立Cache時,EhCache便會采用<defalutCache/>指定的的管理政策
以下屬性是必須的:
l maxElementsInMemory - 在記憶體中緩存的element的最大數目,數字就是記憶體中資料的條數
l maxElementsOnDisk - 在磁盤上緩存的element的最大數目,若是0表示無窮大
l eternal - 設定緩存的elements是否永遠不過期。如果為true,則緩存的資料始終有效,如果為false那麼還要根據timeToIdleSeconds,timeToLiveSeconds判斷
l overflowToDisk - 設定當記憶體緩存溢出的時候是否将過期的element緩存到磁盤上
以下屬性是可選的:
l timeToIdleSeconds - 當緩存在EhCache中的資料前後兩次通路的時間超過timeToIdleSeconds的屬性取值時,這些資料便會删除,預設值是0,也就是可閑置時間無窮大
l timeToLiveSeconds - 緩存element的有效生命期,預設是0.,也就是element存活時間無窮大
diskSpoolBufferSizeMB 這個參數設定DiskStore(磁盤緩存)的緩存區大小.預設是30MB.每個Cache都應該有自己的一個緩沖區.
l diskPersistent - 在VM重新開機的時候是否啟用磁盤儲存EhCache中的資料,預設是false。
l diskExpiryThreadIntervalSeconds - 磁盤緩存的清理線程運作間隔,預設是120秒。每個120s,相應的線程會進行一次EhCache中資料的清理工作
l memoryStoreEvictionPolicy - 當記憶體緩存達到最大,有新的element加入的時候, 移除緩存中element的政策。預設是LRU(最近最少使用),可選的有LFU(最不常使用)和FIFO(先進先出)
-->
<cache-ref namespace="bean.EmpMapper"/>