二級緩存和一級緩存不一樣的地方在於,二級緩存針對的是MapperStament,比一級緩存更細!
首先在使用一級緩存之前,mapper.xml文件中要配置標籤來開啓二級緩存,而且可以細化到mapper.xml中的每條sql語句,比如
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
<select ... flushCache="false" useCache="true"/>
這個更高級的配置創建了一個 FIFO 緩存,並每隔 60 秒刷新,存數結果對象或列表的 512 個引用,而且返回的對象被認爲是隻讀的,因此在不同線程中的調用者之間修改它們會 導致衝突。
可用的收回策略有:
LRU – 最近最少使用的:移除最長時間不被使用的對象。
FIFO – 先進先出:按對象進入緩存的順序來移除它們。
SOFT – 軟引用:移除基於垃圾回收器狀態和軟引用規則的對象。
WEAK – 弱引用:更積極地移除基於垃圾收集器狀態和弱引用規則的對象。
默認的是 LRU。
flushInterval(刷新間隔)可以被設置爲任意的正整數,而且它們代表一個合理的毫秒 形式的時間段。默認情況是不設置,也就是沒有刷新間隔,緩存僅僅調用語句時刷新。
size(引用數目)可以被設置爲任意正整數,要記住你緩存的對象數目和你運行環境的 可用內存資源數目。默認值是 1024。
readOnly(只讀)屬性可以被設置爲 true 或 false。只讀的緩存會給所有調用者返回緩 存對象的相同實例。因此這些對象不能被修改。這提供了很重要的性能優勢。可讀寫的緩存 會返回緩存對象的拷貝(通過序列化) 。這會慢一些,但是安全,因此默認是 false。
先來看
下配置階段的解析mapper.xml文件中的一段代碼
public Cache useNewCache(Class<? extends Cache> typeClass,
Class<? extends Cache> evictionClass,
Long flushInterval,
Integer size,
boolean readWrite,
boolean blocking,
Properties props) {
Cache cache = new CacheBuilder(currentNamespace)
.implementation(valueOrDefault(typeClass, PerpetualCache.class))
.addDecorator(valueOrDefault(evictionClass, LruCache.class))
.clearInterval(flushInterval)
.size(size)
.readWrite(readWrite)
.blocking(blocking)
.properties(props)
.build();
//將生成好的緩存存儲於MapperBuilder和Configuration中,當然此時緩存中時沒有數據的,這裏又用到了裝飾模式進行功能增強,而且套了好幾層
configuration.addCache(cache);
currentCache = cache;
return cache;
}
那麼 最終會根據各自配置的緩存放至到對應的MapperStatement中,也就是說每個mapper都對應有一個自己的緩存,是不是比一級緩存更細了!
好了
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
//獲取緩存對象,看是否開啓狀態
Cache cache = ms.getCache();
if (cache != null) {
flushCacheIfRequired(ms);
//判斷是否使用緩存
if (ms.isUseCache() && resultHandler == null) {
ensureNoOutParams(ms, parameterObject, boundSql);
@SuppressWarnings("unchecked")
//獲取緩存數據
List<E> list = (List<E>) tcm.getObject(cache, key);
//爲空?
if (list == null) {
list = delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
//將獲得的數據進入緩存
tcm.putObject(cache, key, list); // issue #578 and #116
}
return list;
}
}
//繼續query 在這裏會從一級緩存或者數據庫獲取數據
return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
上面可以看出,先後順序是先二級緩存>一級緩存>數據庫獲取
我們具體看看一級緩存的內容
先來看下Cache接口
public interface Cache {
//唯一標示
String getId();
//插入
void putObject(Object key, Object value);
//獲取
Object getObject(Object key);
//移除
Object removeObject(Object key);
//清空
void clear();
//個數
int getSize();
//讀寫鎖
ReadWriteLock getReadWriteLock();
}
哦,好多裝飾類,其實都是簡單的
BlockingCache:增加 可阻塞功能,會在查詢緩存時鎖住對應的Key,如果緩存命中了則會釋放對應的鎖,否則會在查詢數據庫以後再釋放鎖,這樣可以阻止併發情況下多個線程同時查詢數據
FifoCache:增加fifo功能,即先進先出功能-根據固定的長度,默認爲1204 ,如果超出則將第一個移除,在新增
LoggingCache : 增加日誌功能-調用方法先日誌
LruCache:增加lru功能及最近最少使用。使用LinkedHashMap實現
ScheduledCache:增加任務調度功能,超過時間,則自動清除
SerializedCache:增加序列化功能
SoftCache:增加基於軟引用實現的緩存管理策略
SynchronizedCache:增加同步功能
TransactionalCache:增加事務功能
WeakCache:增加弱引用實現的緩存管理策略
PerpetualCache:真正的緩存類,即被裝飾類