redis做为hibernate的二级缓存

hibernate的二级缓存有好多,像ehcache。不过项目的缓存使用的是redis,而redis官方没有实现hibernate的二级缓存接口,只得本身实现。看看公司的高手如何作的吧。 java

先看配置: redis

<!-- 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。它的代码: spring

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: app

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实现: async

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的这个接口的定义: ide

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中实现了。看代码: post

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注释有点出入: ui

* Called after an item has been inserted (before the transaction completes),
	 * instead of calling evict().
	 * This method is used by "synchronous" concurrency strategies.
总之这块不是很理解。
相关文章
相关标签/搜索