接上一文《Mybatis二級緩存原了解析及如何第三方擴充二級緩存》,我們來看二級緩存的事物管理器:
//CachingExecutor
private TransactionalCacheManager tcm = new TransactionalCacheManager();
//查詢時:
public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
throws SQLException {
Cache cache = ms.getCache();
//預設情況下是沒有開啟緩存的(二級緩存).要開啟二級緩存,你需要在你的 SQL 映射檔案中添加一行: <cache/>
//簡單的說,就是先查CacheKey,查不到再委托給實際的執行器去查
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;
}
}
return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
}
//緩存事物管理器
public class TransactionalCacheManager {
//管理了許多TransactionalCache
private Map<Cache, TransactionalCache> transactionalCaches = new HashMap<Cache, TransactionalCache>();
public void clear(Cache cache) {
getTransactionalCache(cache).clear();
}
//得到某個TransactionalCache的值
public Object getObject(Cache cache, CacheKey key) {
return getTransactionalCache(cache).getObject(key);
}
public void putObject(Cache cache, CacheKey key, Object value) {
//擷取TransactionalCache然後存放到entriesToAddOnCommit中
getTransactionalCache(cache).putObject(key, value);
}
//送出時全部送出
public void commit() {
for (TransactionalCache txCache : transactionalCaches.values()) {
txCache.commit();
}
}
//復原時全部復原
public void rollback() {
for (TransactionalCache txCache : transactionalCaches.values()) {
txCache.rollback();
}
}
//這裡擷取事務緩存,如果沒有則建立新的,并把我們的緩存作為包裝放進去(裝飾者設計模式)
private TransactionalCache getTransactionalCache(Cache cache) {
TransactionalCache txCache = transactionalCaches.get(cache);
if (txCache == null) {
txCache = new TransactionalCache(cache);
transactionalCaches.put(cache, txCache);
}
return txCache;
}
}
public class TransactionalCache implements Cache {
//我們真實的緩存
private Cache delegate;
//commit時要不要清緩存
private boolean clearOnCommit;
//commit時要添加的元素
private Map<Object, Object> entriesToAddOnCommit;
//自定義中未命中的
private Set<Object> entriesMissedInCache;
public TransactionalCache(Cache delegate) {
this.delegate = delegate;
//預設commit時不清緩存
this.clearOnCommit = false;
this.entriesToAddOnCommit = new HashMap<Object, Object>();
this.entriesMissedInCache = new HashSet<Object>();
}
@Override
public String getId() {
return delegate.getId();
}
@Override
public int getSize() {
return delegate.getSize();
}
@Override
public Object getObject(Object key) {
// issue #116
Object object = delegate.getObject(key);
if (object == null) {
entriesMissedInCache.add(key);
}
// issue #146
if (clearOnCommit) {
return null;
} else {
return object;
}
}
@Override
public ReadWriteLock getReadWriteLock() {
return null;
}
@Override
public void putObject(Object key, Object object) {
//存放到臨時變量中
entriesToAddOnCommit.put(key, object);
}
@Override
public Object removeObject(Object key) {
return null;
}
@Override
public void clear() {
clearOnCommit = true;
entriesToAddOnCommit.clear();
}
//多了commit方法,提供事務功能
public void commit() {
if (clearOnCommit) {
delegate.clear();
}
//開始重新整理放到真正的緩存中
flushPendingEntries();
//重置臨時緩存
reset();
}
public void rollback() {
unlockMissedEntries();
reset();
}
private void reset() {
clearOnCommit = false;
entriesToAddOnCommit.clear();
entriesMissedInCache.clear();
}
//這裡是将事務管理器中的臨時緩存放到我們真實的緩存中
private void flushPendingEntries() {
for (Map.Entry<Object, Object> entry : entriesToAddOnCommit.entrySet()) {
delegate.putObject(entry.getKey(), entry.getValue());
}
for (Object entry : entriesMissedInCache) {
if (!entriesToAddOnCommit.containsKey(entry)) {
delegate.putObject(entry, null);
}
}
}
private void unlockMissedEntries() {
for (Object entry : entriesMissedInCache) {
delegate.putObject(entry, null);
}
}
}
當我們存放緩存的時候會先臨時存放到transactionalCache臨時緩存中。
看了上面的代碼我們知道:隻有當TransactionalCacheManager的commit方法調用時,才會将transactionalCache中的資料存放到我們真實的緩存中。TransactionalCacheManager的commit方法被調用隻有在sqlSession.commit()時或者sqlSession.close()時。