天天看點

hibernate二級緩存(二)二級緩存實作原理簡單剖析hibernate二級緩存(二)二級緩存實作原理簡單剖析

hibernate二級緩存(二)二級緩存實作原理簡單剖析

在前面我們将過hibernate二級緩存類似于一個插件,将緩存的具體實作分離,緩存的具體實作是通過hibernate.cache.region.factory_class參數配置指定。本文隻是對hibernate二級緩存的部分接口進行簡單的解析,大緻了解二級緩存的整體結構,二級緩存的内部實作很複雜,如要深究請閱讀hibernate源碼。

1. hibernate二級緩存結構

hibernate二級緩存涉及到如下幾個重要的接口:

  • RegionFactory
  • DomainDataRegion
  • EntityDataAccess
  • StorageAccess

1.1 RegionFactory擷取緩存的工廠,RegionFactory有如下幾個重要的方法:

public interface RegionFactory extends Service, Stoppable {
	//初始化方法
	void start(SessionFactoryOptions settings, Map configValues) throws CacheException;
	
	boolean isMinimalPutsEnabledByDefault();

	//緩存政策
	AccessType getDefaultAccessType();

	String qualify(String regionName);

	default CacheTransactionSynchronization createTransactionContext(SharedSessionContractImplementor session) {
		return new StandardCacheTransactionSynchronization( this );
	}
	
	long nextTimestamp();

	default long getTimeout() {
		// most existing providers defined this as 60 seconds.
		return 60000;
	}

	DomainDataRegion buildDomainDataRegion(
			DomainDataRegionConfig regionConfig,
			DomainDataRegionBuildingContext buildingContext);

	QueryResultsRegion buildQueryResultsRegion(String regionName, SessionFactoryImplementor sessionFactory);

	TimestampsRegion buildTimestampsRegion(String regionName, SessionFactoryImplementor sessionFactory);
}
           
  • start 初始化RegionFactory
  • getDefaultAccessType 獲得緩存政策,前面說過的CacheConcurrencyStrategy内部是基于AccessType
  • nextTimestamp 生成時間戳,用于時間戳緩存
  • buildDomainDataRegion 建立一個實體領域模型的Region,使用該對象來緩存實體,可以了解為實體緩存的holder
  • buildQueryResultsRegion 建立查詢緩存
  • buildTimestampsRegion 建立時間戳緩存。時間戳緩存Region存放了對于查詢結果相關的表進行插入, 更新或删除操作的時間戳。Hibernate 通過時間戳緩存Region來判斷被緩存的查詢結果是否過期

RegionFactory 是建立緩存的工廠,所有的緩存都是通過RegionFactory 來擷取的,而RegionFactory 是在EnabledCaching構造方法中初始化的。RegionFactory 的初始化過程如下圖所示:

hibernate二級緩存(二)二級緩存實作原理簡單剖析hibernate二級緩存(二)二級緩存實作原理簡單剖析

sessionFactory = new MetadataSources(registry).buildMetadata().buildSessionFactory();可以看到是在初始化sessionFactory的時候來初始化RegionFactory 。

1.2 DomainDataRegion緩存區域

DomainDataRegion可以了解為緩存的wraper或者holder。接口如下:

public interface DomainDataRegion extends Region {

    EntityDataAccess getEntityDataAccess(NavigableRole var1);

    NaturalIdDataAccess getNaturalIdDataAccess(NavigableRole var1);

    CollectionDataAccess getCollectionDataAccess(NavigableRole var1);
}
           

getEntityDataAccess 獲得一個EntityDataAccess,對緩存的操作實際上是代理給EntityDataAccess,由EntityDataAccess來真正的管理緩存。

1.3 EntityDataAccess 緩存實際的通路者,用于管理對緩存實體資料的事務性和并發通路的協定

public interface EntityDataAccess extends CachedDomainDataAccess {
	//生成緩存key
	Object generateCacheKey(
			Object id,
			EntityPersister rootEntityDescriptor,
			SessionFactoryImplementor factory,
			String tenantIdentifier);
	//擷取緩存key
	Object getCacheKeyId(Object cacheKey);
	//在查詢後,是否将查詢結果插入緩存
	boolean insert(SharedSessionContractImplementor session, Object key, Object value, Object version);
	//插入後是否更新緩存
	boolean afterInsert(SharedSessionContractImplementor session, Object key, Object value, Object version);
	
	boolean update(
			SharedSessionContractImplementor session,
			Object key,
			Object value,
			Object currentVersion,
			Object previousVersion);

	boolean afterUpdate(
			SharedSessionContractImplementor session,
			Object key,
			Object value,
			Object currentVersion,
			Object previousVersion,
			SoftLock lock);
}

           

在上面的接口中我們并沒有看到實際操作緩存的接口,那麼EntityDataAccess 又是怎麼通路和管理緩存的呢,下面來看一下EntityDataAccess 的接口繼承和實作關系:

hibernate二級緩存(二)二級緩存實作原理簡單剖析hibernate二級緩存(二)二級緩存實作原理簡單剖析

從上面的圖我們可以看到EntityDataAccess 有一個抽象類,4個實作類。4個實作類分别對應了4中緩存通路類型,READ_ONLY,TRANSACTIONAL,READ_WRITE,NONSTRICT_READ_WRITE。抽象類AbstractEntityDataAccess實作了

EntityDataAccess 繼承了AbstractCachedDomainDataAccess。AbstractCachedDomainDataAccess的部分代碼如下:

hibernate二級緩存(二)二級緩存實作原理簡單剖析hibernate二級緩存(二)二級緩存實作原理簡單剖析

AbstractCachedDomainDataAccess裡面包含了一個DomainDataStorageAccess,DomainDataStorageAccess繼承自StorageAccess,StorageAccess的接口如下:

public interface StorageAccess {
	/**
	 * Get an item from the cache.
	 */
	Object getFromCache(Object key, SharedSessionContractImplementor session);

	/**
	 * Put an item into the cache
	 */
	void putIntoCache(Object key, Object value, SharedSessionContractImplementor session);

	/**
	 * Remove an item from the cache by key
	 */
	default void removeFromCache(Object key, SharedSessionContractImplementor session) {
		evictData( key );
	}

	/**
	 * Clear data from the cache
	 */
	default void clearCache(SharedSessionContractImplementor session) {
		evictData();
	}

	/**
	 * Does the cache contain this key?
	 */
	boolean contains(Object key);

	/**
	 * Clear all data regardless of transaction/locking
	 */
	void evictData();

	/**
	 * Remove the entry regardless of transaction/locking
	 */
	void evictData(Object key);

	/**
	 * Release any resources.  Called during cache shutdown
	 */
	void release();
}

           

到這裡我們終于發現了操作緩存的實際接口。而DomainDataStorageAccess接口是緩存操作必須實作的接口,ehchche的實作類是StorageAccessImpl。當然通過擴充該接口我們還可以将緩存放置到redis,memcache。

2. 緩存的初始化和調用

2.1 從上面的接口關系我們大緻可以得到如下的一個緩存初始化關系鍊:

sessionFactory ----->EnabledCaching ----->RegionFactory ----->DomainDataRegion ----->EntityDataAccess ----->AbstractEntityDataAccess ----->AbstractCachedDomainDataAccess ----->StorageAccess

2.2 擷取cache的調用棧

hibernate二級緩存(二)二級緩存實作原理簡單剖析hibernate二級緩存(二)二級緩存實作原理簡單剖析

SessionImpl擷取實體對象,然後通過一系列的調用,最終會落到AbstractCachedDomainDataAccess.get(SharedSessionContractImplementor session, Object key),前面已經說過該方法實際上是調用的DomainDataStorageAccess.getFromCache( key, session )。

3. 自定義hibernate緩存

通過前面的一系列分析,我們大緻了解了hibernate緩存的一些重要的接口。如果要自定義hibernate緩存那麼我們必須實作上面的這些接口。好在hibernate内部為實作了大多數的擴充,我們隻需要擴充RegionFactory和DomainDataStorageAccess接口既可以自定義hibernate的二級緩存。

hibernate為實作RegionFactory提供了一個模版類RegionFactoryTemplate,我們直接通過實作該類和DomainDataStorageAccess,即可自定義hibernate二級緩存。

繼續閱讀