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.總之這塊不是很理解。