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.
總之這塊不是很理解。
相關文章
相關標籤/搜索