在前面我们将过hibernate二级缓存类似于一个插件,将缓存的具体实现分离,缓存的具体实现是通过hibernate.cache.region.factory_class参数配置指定。本文只是对hibernate二级缓存的部分接口进行简单的解析,大致了解二级缓存的整体结构,二级缓存的内部实现很复杂,如要深究请阅读hibernate源码。
hibernate二级缓存涉及到如下几个重要的接口:
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);
}
RegionFactory 是创建缓存的工厂,所有的缓存都是通过RegionFactory 来获取的,而RegionFactory 是在EnabledCaching构造方法中初始化的。RegionFactory 的初始化过程如下图所示:
sessionFactory = new MetadataSources(registry).buildMetadata().buildSessionFactory();可以看到是在初始化sessionFactory的时候来初始化RegionFactory 。
DomainDataRegion可以理解为缓存的wraper或者holder。接口如下:
public interface DomainDataRegion extends Region {
EntityDataAccess getEntityDataAccess(NavigableRole var1);
NaturalIdDataAccess getNaturalIdDataAccess(NavigableRole var1);
CollectionDataAccess getCollectionDataAccess(NavigableRole var1);
}
getEntityDataAccess 获得一个EntityDataAccess,对缓存的操作实际上是代理给EntityDataAccess,由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 的接口继承和实现关系:
从上面的图我们可以看到EntityDataAccess 有一个抽象类,4个实现类。4个实现类分别对应了4中缓存访问类型,READ_ONLY,TRANSACTIONAL,READ_WRITE,NONSTRICT_READ_WRITE。抽象类AbstractEntityDataAccess实现了
EntityDataAccess 继承了AbstractCachedDomainDataAccess。AbstractCachedDomainDataAccess的部分代码如下:
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。
sessionFactory ----->EnabledCaching ----->RegionFactory ----->DomainDataRegion ----->EntityDataAccess ----->AbstractEntityDataAccess ----->AbstractCachedDomainDataAccess ----->StorageAccess
SessionImpl获取实体对象,然后通过一系列的调用,最终会落到AbstractCachedDomainDataAccess.get(SharedSessionContractImplementor session, Object key),前面已经说过该方法实际上是调用的DomainDataStorageAccess.getFromCache( key, session )。
通过前面的一系列分析,我们大致了解了hibernate缓存的一些重要的接口。如果要自定义hibernate缓存那么我们必须实现上面的这些接口。好在hibernate内部为实现了大多数的扩展,我们只需要扩展RegionFactory和DomainDataStorageAccess接口既可以自定义hibernate的二级缓存。
hibernate为实现RegionFactory提供了一个模版类RegionFactoryTemplate,我们直接通过实现该类和DomainDataStorageAccess,即可自定义hibernate二级缓存。