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.总之这块不是很理解。