正如大多數持久層框架同樣,MyBatis 一樣提供了一級緩存和二級緩存的支持 php
可能你會有疑惑,個人mybatis bean是由spring 來管理的,已經屏蔽了sqlSession這個東西了?那怎麼的一次操做纔算是一次sqlSession呢?java
spring整合mybatis後,非事務環境下,每次操做數據庫都使用新的sqlSession對象。所以mybatis的一級緩存沒法使用(一級緩存針對同一個sqlsession有效)redis
在開啓事物的狀況之下,spring使用threadLocal獲取當前資源綁定同一個sqlSession,所以此時一級緩存是有效的 在開啓以及緩存的時候查詢獲得的對象是同一個對象。 這種狀況下會出現一個問題。咱們先看一下代碼。算法
public void listMybatisModel() {
List<MybatisModel> mybatisModels = mapper.listMybatisModel();
List<MybatisModel> mybatisModelsOther = mapper.listMybatisModel();
System.out.println(mybatisModels == mybatisModelsOther);
System.out.println("list count: " + mybatisModels.size());
}
複製代碼
System.out.println(mybatisModels == mybatisModelsOther);
複製代碼
輸出結果居然是true,這樣說來是同一個對象。 會出現這種場景,第一次查出來的對象而後修改了,第二次查出來的就是修改後的對象。spring
對SqlSession的操做mybatis內部都是經過Executor來執行的。Executor的生命週期和SqlSession是一致的。Mybatis在Executor中建立了一級緩存,基於PerpetualCache 類的 HashMapsql
用下面這張圖描述一級緩存和二級緩存的關係。數據庫
// mybatis-config.xml 中配置
<settings>
默認值爲 true。即二級緩存默認是開啓的
<setting name="cacheEnabled" value="true"/>
</settings>
// 具體mapper.xml 中配置
<mapper namespace="cn.itcast.mybatis.mapper.UserMapper">
<!-- 開啓本mapper的namespace下的二級緩存 type:指定cache接口的實現類的類型,mybatis默認使用PerpetualCache 要和ehcache整合,須要配置type爲ehcache實現cache接口的類型-->
<cache />
</mapper>
複製代碼
若是想要設置增刪改操做的時候不清空二級緩存的話,能夠在其insert或delete或update中添加屬性flushCache=」false」,默認爲 true。apache
<delete id="deleteStudent" flushCache="false">
DELETE FROM user where id=#{id}
</delete>
複製代碼
經過如下一些配置能夠修改一些緩存參數。緩存
<cache eviction= "FIFO" flushlnterval="600000" size="512" readOnly="true" />
複製代碼
配置建立了一個FIFO緩存,並每隔 60 秒刷新一次,存儲集合或對象的512個引用, 並且返回的對象被認爲是隻讀的,所以在不一樣線程中的調用者之間修改它們會致使衝突。cache能夠配置的屬性以下。 eviction (收回策略)安全
LRU (最近最少使用的: 移除最長時間不被使用的對象,這是默認值 。 FIFO (先進先出〉 : 按對象進入緩存的順序來移除它們 。 SOFT (軟引用) : 移除基於垃圾回收器狀態和軟引用規則的對象 。 WEAK (弱引用) : 更積極地移除基於垃圾收集器狀態和弱引用規則的對象
@CacheNamespace (
eviction = FifoCache.class ,
flushinterval = 60000 ,
size = 512 ,
readWrite = true)
public interface Mapper {
}
複製代碼
括號內的內容是配置緩存屬性。
@CacheNarnespaceRef(RoleMapper.class)
public interface RoleMapper {
複製代碼
由於想讓 RoleMapper 接口中的註解方法和 XML中的方法使用相同的緩存,所以使用參照緩存配置RoleMapper.class,這樣就會使用命名空間爲xx.xxx.xxx.xxx.RoleMapper的緩存配置,即RoleMapper.xml 中配置的緩存 。 Mapper 接口能夠經過註解引用XML 映射文件或者其餘接口的緩存,在 XML 中也能夠配置參照緩存,如能夠在 RoleMapper.xml 中進行以下修改 。
<cache-ref narnespace="xxx.xxx.xxx.xxx.RoleMapper"/>
複製代碼
這樣配置後XML 就會引用 Mapper 接口中配置的二級緩存,一樣能夠避免同時配置二級緩存致使的衝突。MyBatis 中不多會同時使用 Mapper 接口註解方式和XML映射文件,因此參照緩存並非爲了解決這個問題而設計的。參照緩存除了可以經過引用其餘緩存減小配置外,主要的做用是解決髒讀。
MyBatis使用SerializedCache(org.apache.ibaits.cache.decorators.SerializedCache)序列化緩存來實現可讀寫緩存類,井經過序列化和反序列化來保證經過緩存獲取數據時,獲得的是一個新的實例。所以,若是配置爲只讀緩存,MyBatis就會使用Map來存儲緩存值,這種狀況下,從緩存中獲取的對象就是同一個實例。由於使用可讀寫緩存,可使用SerializedCache序列化緩存。這個緩存類要求全部被序列化的對象必須實現 Serializable (java.io.Serializable)接口 雖然使用序列化獲得的對象都是不同的對象修改時都是互不影響,可是仍是不安全的。
Mybatis的二級緩存是和命名空間綁定的,因此一般狀況下每個Mapper映射文件都有本身的二級緩存,不一樣的mapper的二級緩存互不影響。
緩存數據有內存和磁盤兩級,無須擔憂容量問題。
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.0.3</version>
</dependency>
複製代碼
在 src/main/resources 目錄下新增 ehcache.xml 文件。
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="false" monitoring="autodetect" dynamicConfig="true">
<diskStore path="D:/cache" />
<defaultCache maxElementsInMemory="3000" eternal="false" copyOnRead="true" copyOnWrite="true" timeToIdleSeconds="3600" timeToLiveSeconds="3600" overflowToDisk="true" diskPersistent="true"/>
</ehcache>
複製代碼
有關EhCache的詳細配置能夠參考地址 www.ehcache.org/ehcache.xml 中的內容。
在xml中添加
<cache type ="org.mybatis.caches.ehcache.EhcacheCache" />
複製代碼
只經過設置 type 屬性就可 以使用 EhCache 緩存了,這時cache的其餘屬性都不會起到任何做用,針對緩存的配置都在ehcache.xml中進行。在ehcache.xml配置文件中,只有一個默認的緩存配置,因此配置使用EhCache緩存的Mapper映射文件都會有一個以映射文件命名空間命名的緩存。若是想針對某一個命名空間進行配置,須要在 ehcache.xml 中添加一個和映射文件命名空間一致的緩存配置,例如針對RoleMapper能夠進行以下配置。
<cache name="tk.mybatis.simple.mapper.RoleMapper" maxElementsInMemory="3000" eternal="false" copyOnRead="true" copyOnWrite="true" timeToIdleSeconds="3600" timeToLiveSeconds="3600" overflowToDisk="true" diskPersistent="true"/>
複製代碼
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-redis</artifactId>
<version>1.0.0-beta2</version>
</dependency>
複製代碼
host=localhost
port=6379
connectionTimeout=SOOO
soTimeout=SOOO
password=
database=O
clientName=
複製代碼
<mapper namespace = 」 tk.mybat 工 s.s 工 mple.mapper.RoleMapper 」 〉
<cache type= "org.mybatis.caches.redis.RedisCache" />
〈 !一其餘本身直一 〉
</mapper>
複製代碼
配置依然很簡單, RedisCache 在保存緩存數據和獲取緩存數據時,使用了Java的序列化和反序列化,所以還須要保證被緩存的對象必須實現Serializable接口。改成RedisCache緩存配置後, testL2Cache 測試第一次執行時會所有成功,可是若是再次執行,就會出錯。這是由於Redis做爲緩存服務器,它緩存的數據和程序(或測試)的啓動無關,Redis 的緩存並不會由於應用的關閉而失效。因此再次執行時沒有進行一次數據庫查詢,全部查詢都使用緩存,測試的第一部分代碼中的rolel和role2都是直接從二級緩存中獲取數據,由於是可讀寫緩存,因此不是相同的對象。當須要分佈式部署應用時,若是使用MyBatis自帶緩存或基礎的EhCahca緩存,分佈式應用會各自擁有本身的緩存,它們之間不會共享緩存 ,這種方式會消耗更多的服務器資源。若是使用相似 Redis 的緩存服務,就能夠將分佈式應用鏈接到同一個緩存服務器,實現分佈式應用間的緩存共享 。