hibernate的二级缓存有好多,像ehcache。不过项目的缓存使用的是redis,而redis官方没有实现hibernate的二级缓存接口,只得自己实现。看看公司的高手如何做的吧。
先看配置:
<!-- entityManagerFactory -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
depends-on="cacheManagerFactory">
...
<property name="jpaProperties">
<props>
...
<prop key="hibernate.cache.use_second_level_cache">true</prop>
<!-- <prop key="hibernate.cache.use_query_cache">true</prop> -->
<prop key="hibernate.cache.region.factory_class">xxx.xxx.framework.cache.hibernate.CacheRegionFactory</prop>
...
</props>
</property>
</bean>
<!-- cache -->
<bean id="cacheManager" class="xxx.xxx.framework.cache.redis.RedisCacheManager">
<property name="connectionFactory" ref="redisConnectionFactory"/>
<property name="namespace" value="payment"/>
</bean>
<bean id="cacheManagerFactory" class="xxx.xxx.framework.cache.hibernate.CacheManagerFactory">
<property name="cacheManager" ref="cacheManager"/>
</bean>
cacheManager是redis缓存的配置,二级缓存实现类CacheRegionFactory里面会用到它,但是hibernate缓存配置的只是配置实现类,没法注入CacheRegionFactory对象,所以这边多了个cacheManagerFactory,注意配置中的depends-on。它的代码:
public final class CacheManagerFactory implements DisposableBean {
private static CacheManager CACH_EMANAGER;
public void setCacheManager(CacheManager cacheManager) {
CACH_EMANAGER = cacheManager;
}
public static CacheManager getCacheManager() {
return CACH_EMANAGER;
}
@Override
public void destroy() throws Exception {
CACH_EMANAGER = null;
}
}
它就是负责生成cacheManager。注意getCacheManager是静态方法。然后我们看redis二级缓存的实现类:
public class CacheRegionFactory implements RegionFactory {
private static final long serialVersionUID = -1557439471733872383L;
private CacheManager cacheManager;
private static final AtomicLong CURRENT = new AtomicLong();
protected Settings settings;
@Override
public void start(Settings settings, Properties properties) throws CacheException {
cacheManager = CacheManagerFactory.getCacheManager();
this.settings = settings;
Assert.notNull(cacheManager, "cacheManager is required,CacheManagerFactory must be init first");
}
@Override
public void stop() {
cacheManager = null;
}
@Override
public boolean isMinimalPutsEnabledByDefault() {
return true;
}
@Override
public AccessType getDefaultAccessType() {
return AccessType.NONSTRICT_READ_WRITE;
}
@Override
public long nextTimestamp() {
return CURRENT.incrementAndGet();
}
@Override
public EntityRegion buildEntityRegion(String regionName, Properties properties, CacheDataDescription metadata) throws CacheException {
return new EntityRegionImpl(regionName, cacheManager.getCache(shortRegionName(regionName)), settings, properties, metadata);
}
@Override
public NaturalIdRegion buildNaturalIdRegion(String regionName, Properties properties, CacheDataDescription metadata) throws CacheException {
return new NaturalIdRegionImpl(regionName, cacheManager.getCache(shortRegionName(regionName)), settings, properties, metadata);
}
@Override
public CollectionRegion buildCollectionRegion(String regionName, Properties properties, CacheDataDescription metadata) throws CacheException {
return new CollectionRegionImpl(regionName, cacheManager.getCache(shortRegionName(regionName)), settings, properties, metadata);
}
@Override
public QueryResultsRegion buildQueryResultsRegion(String regionName, Properties properties) throws CacheException {
return new QueryResultsRegionImpl(regionName, cacheManager.getCache(shortRegionName(regionName)), settings, properties);
}
@Override
public TimestampsRegion buildTimestampsRegion(String regionName, Properties properties) throws CacheException {
return new TimestampsRegionImpl(regionName, cacheManager.getCache(shortRegionName(regionName)), settings, properties);
}
private static String shortRegionName(String regionName) {
...
}
}
RegionFactory是hibernate的接口。在start里面通过调用CacheManagerFactory来获取redis的CacheManager。这里主要还是buildXX的几个方法,在这些方法里面通过
CacheManager和region获取cache,然后再生成hibernate需要的缓存对象。
以buildEntityRegion为例,返回的对象EntityRegion是hibernate的接口,EntityRegionImpl怎是我们的实现。先看看它的实现:
public class EntityRegionImpl extends AbstractTransactionalDataRegion implements EntityRegion {
public EntityRegionImpl(String regionName, Cache cache, Settings settings, Properties properties, CacheDataDescription metadata) {
super(regionName,cache, settings, properties, metadata);
}
@Override
public EntityRegionAccessStrategy buildAccessStrategy(AccessType accessType) throws CacheException {
return new EntityRegionAccessStrategyImpl(this, settings);
}
}
这边可以分两部分看了一个是它的父类AbstractTransactionalDataRegion,另外一个是由EntityRegion继承而来的buildAccessStrategy(Hibernate EntityRegion的接口实现)。
先看它的父类,父类实现了hibernate的TansactionalDateRegion:
public class AbstractTransactionalDataRegion extends AbstractRegion implements TransactionalDataRegion {
private final CacheDataDescription metadata;
public AbstractTransactionalDataRegion(String regionName, Cache cache, Settings settings, Properties properties, CacheDataDescription metadata) {
super(regionName, cache, settings, properties);
this.metadata = metadata;
}
@Override
public boolean isTransactionAware() {
return false;
}
@Override
public CacheDataDescription getCacheDataDescription() {
return metadata;
}
}
isTransactionAware是表示是否支持jta事务。然后再看它的父类AbstractRegion:
public class AbstractRegion implements Region {
private static final AtomicLong CURRENT = new AtomicLong();
private String regionName;
protected final Cache cache;
protected final Properties properties;
protected final Settings settings;
protected final KeyGenerator keyGenerator = DefaultKeyGenerator.INSTANCE;
public AbstractRegion(String regionName, Cache cache, Settings settings, Properties properties) {
this.regionName = regionName;
this.cache = cache;
this.settings = settings;
this.properties = properties;
}
@Override
public String getName() {
return regionName;
}
@Override
public void destroy() throws CacheException {
}
@Override
public boolean contains(Object key) {
return cache.exists(key);
}
@Override
public long getSizeInMemory() {
return -1;
}
@Override
public long getElementCountInMemory() {
return cache.getStatistics().getSize();
}
@Override
public long getElementCountOnDisk() {
return -1;
}
@Override
public Map toMap() {
return Collections.EMPTY_MAP;
}
@Override
public long nextTimestamp() {
return CURRENT.incrementAndGet();
}
@Override
public int getTimeout() {
return 300;
}
public Object get(Object key) throws CacheException {
try {
return postGet(cache.get(toKey(key)));
} catch (Throwable e) {
throw new CacheException(e);
}
}
public void put(Object key, Object value) throws CacheException {
try {
cache.put(toKey(key), value);
} catch (Exception e) {
throw new CacheException(e);
}
}
public void evict(Object key) throws CacheException {
try {
cache.evict(toKey(key));
} catch (Exception e) {
throw new CacheException(e);
}
}
public void evictAll() throws CacheException {
try {
cache.clear();
} catch (Exception e) {
throw new CacheException(e);
}
}
private Object toKey(Object key) {
if (key instanceof CacheKey) {
key = ((CacheKey) key).getKey();
}
return keyGenerator.generate(key);
}
......
这里实现了hibernate region的一些接口,另外一些对于缓存的一些操作方法如:put\get\evict等也做了实现。
现在我们回过头看看EntityRegionImpl里面的EntityRegionAccessStrategyImpl实现:
public class EntityRegionAccessStrategyImpl extends AbstractAccessStrategy<EntityRegionImpl> implements EntityRegionAccessStrategy {
public EntityRegionAccessStrategyImpl(EntityRegionImpl region, Settings settings) {
super(region, settings);
}
@Override
public EntityRegion getRegion() {
return region;
}
}
它也实现了hibernate的接口:EntityRegionAccessStrategy,字面意思就是缓存的访问策略。
先看看hibernate的这个接口的定义:
public interface EntityRegionAccessStrategy extends RegionAccessStrategy{
/**
* Get the wrapped entity cache region
*
* @return The underlying region
*/
public EntityRegion getRegion();
/**
* Called after an item has been inserted (before the transaction completes),
* instead of calling evict().
* This method is used by "synchronous" concurrency strategies.
*
* @param key The item key
* @param value The item
* @param version The item's version value
* @return Were the contents of the cache actual changed by this operation?
* @throws CacheException Propogated from underlying {@link org.hibernate.cache.spi.Region}
*/
public boolean insert(Object key, Object value, Object version) throws CacheException;
/**
* Called after an item has been inserted (after the transaction completes),
* instead of calling release().
* This method is used by "asynchronous" concurrency strategies.
*
* @param key The item key
* @param value The item
* @param version The item's version value
* @return Were the contents of the cache actual changed by this operation?
* @throws CacheException Propogated from underlying {@link org.hibernate.cache.spi.Region}
*/
public boolean afterInsert(Object key, Object value, Object version) throws CacheException;
/**
* Called after an item has been updated (before the transaction completes),
* instead of calling evict(). This method is used by "synchronous" concurrency
* strategies.
*
* @param key The item key
* @param value The item
* @param currentVersion The item's current version value
* @param previousVersion The item's previous version value
* @return Were the contents of the cache actual changed by this operation?
* @throws CacheException Propogated from underlying {@link org.hibernate.cache.spi.Region}
*/
public boolean update(Object key, Object value, Object currentVersion, Object previousVersion) throws CacheException;
/**
* Called after an item has been updated (after the transaction completes),
* instead of calling release(). This method is used by "asynchronous"
* concurrency strategies.
*
* @param key The item key
* @param value The item
* @param currentVersion The item's current version value
* @param previousVersion The item's previous version value
* @param lock The lock previously obtained from {@link #lockItem}
* @return Were the contents of the cache actual changed by this operation?
* @throws CacheException Propogated from underlying {@link org.hibernate.cache.spi.Region}
*/
public boolean afterUpdate(Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock) throws CacheException;
}
它的父接口里面还有一些其他的接口如get,remove等可以直接看看源码注释上面都写了。
我们的实现类同时有个父类AbstractAccessStrategy<EntityRegionImpl>,很多EntityRegionAccessStrategy因为都是公用的所以在AbstractAccessStrategy中实现了。看代码:
public abstract class AbstractAccessStrategy<T extends AbstractTransactionalDataRegion> {
protected final T region;
protected final Settings settings;
public AbstractAccessStrategy(T region, Settings settings) {
this.region = region;
this.settings = settings;
}
public boolean insert(Object key, Object value) throws CacheException {
return insert(key, value, null);
}
public boolean insert(Object key, Object value, Object version) throws CacheException {
return false;
}
public boolean afterInsert(Object key, Object value) throws CacheException {
return afterInsert(key, value, null);
}
public boolean afterInsert(Object key, Object value, Object version) throws CacheException {
return false;
}
public boolean update(Object key, Object value) throws CacheException {
return update(key, value, null, null);
}
public boolean update(Object key, Object value, Object currentVersion, Object previousVersion) throws CacheException {
remove(key);
return false;
}
public boolean afterUpdate(Object key, Object value, SoftLock lock) throws CacheException {
return afterUpdate(key, value, null, null, lock);
}
public boolean afterUpdate(Object key, Object value, Object currentVersion, Object previousVersion, SoftLock lock) throws CacheException {
unlockItem(key, lock);
return false;
}
public Object get(Object key, long txTimestamp) throws CacheException {
return region.get(key);
}
public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version) throws CacheException {
return putFromLoad(key, value, txTimestamp, version, settings.isMinimalPutsEnabled());
}
public boolean putFromLoad(Object key, Object value, long txTimestamp, Object version, boolean minimalPutOverride) throws CacheException {
if (minimalPutOverride && region.contains(key)) {
return false;
} else {
region.put(key, value);
return true;
}
}
public SoftLock lockItem(Object key, Object version) throws CacheException {
return null;
}
public SoftLock lockRegion() throws CacheException {
return null;
}
public void unlockItem(Object key, SoftLock lock) throws CacheException {
region.evict(key);
}
public void unlockRegion(SoftLock lock) throws CacheException {
region.evictAll();
}
public void remove(Object key) throws CacheException {
region.evict(key);
}
public void removeAll() throws CacheException {
region.evictAll();
}
public void evict(Object key) throws CacheException {
region.evict(key);
}
public void evictAll() throws CacheException {
region.evictAll();
}
}
上面的方法有些怪异如insert方法,里面就直接返回false了,问了高手,他说参考ehcache的实现。看了下ehcache的源码它上面写了:A no-op since this is an asynchronous cache access strategy。这个和hibernate接口定义的insert注释有点出入:
* Called after an item has been inserted (before the transaction completes),
* instead of calling evict().
* This method is used by "synchronous" concurrency strategies.
总之这块不是很理解。
来源:oschina
链接:https://my.oschina.net/u/918405/blog/102216