天天看點

Mybatis二級緩存之緩存事物管理器(TransactionalCacheManager)

作者:王朋code

接上一文《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()時。

繼續閱讀