背景
这是我的工作(简化)GenericDao界面,可通过任何方式实现DomainDao:
GenericDao
DomainDao
GenericDao.java
@NoRepositoryBean public interface GenericDao<E extends Persistable<K>, K extends Serializable> extends JpaRepository<E, K> { public List<E> findAll(); public E persist(E entity); }
GenericDaoImpl.java
public class GenericDaoImpl<E extends Persistable<K>, K extends Serializable> extends SimpleJpaRepository<E, K> implements GenericDao<E, K> { private final JpaEntityInformation<E, ?> entityInformation; private final EntityManager em; private final Class<E> type; public GenericDaoImpl(JpaEntityInformation<E, ?> entityInformation, EntityManager em) { super(entityInformation, em); this.entityInformation = entityInformation; this.em = em; this.type = entityInformation.getJavaType(); } @Override public List<E> findAll() { return super.findAll(); } @Override @Transactional public E persist(E entity) { if (entityInformation.isNew(entity) || !EntityUtils.isPrimaryKeyGenerated(type) && !em.contains(entity)) { em.persist(entity); } return entity; } }
例如,要管理Foo和Bar,只需创建两个接口,如下所示:
Foo
Bar
FooDao.java
public interface FooDao extends GenericDao<Foo, Integer> { }
BarDao.java
public interface BarDao extends GenericDao<Bar, Integer> { }
的@Autowired注释Spring将自动GenericDaoImpl使用良好的实体和主键类型实例化一个。
@Autowired
Spring
GenericDaoImpl
问题
我现在正在尝试使用EhCache和EhCache Spring Annotations模型在DAO上添加一个缓存过程。
@NoRepositoryBean public interface GenericDao<E extends Persistable<K>, K extends Serializable> extends JpaRepository<E, K> { @Cacheable(cacheName = "dao") public List<E> findAll(); @TriggersRemove(cacheName = "dao") public E persist(E entity); }
applicationContext.xml
<ehcache:annotation-driven cache-manager="ehCacheManager" /> <bean id="ehCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" />
ehcache.xml
<cache name="dao" eternal="false" maxElementsInMemory="10000" overflowToDisk="false" timeToIdleSeconds="86400" timeToLiveSeconds="86400" memoryStoreEvictionPolicy="LFU" />
使用的问题GenericDao是,缓存应该彼此DomainDao独立地进行管理。例如,在当前配置下,如果我先调用fooDao.findAll(),然后再调用,barDao.persist(new Bar())则由生成的缓存fooDao.findAll()将被重置,因为将使用同一缓存(即<cache name="dao" />),而不必使用。
fooDao.findAll()
barDao.persist(new Bar())
<cache name="dao" />
步道
我尝试实现自己的实现CacheKeyGenerator,这将考虑调用的类型DomainDao:
CacheKeyGenerator
<ehcache:annotation-driven cache-manager="ehCacheManager" default-cache-key-generator="daoCacheKeyGenerator" /> <bean id="ehCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" /> <bean id="daoCacheKeyGenerator" class="myapp.dao.support.DaoCacheKeyGenerator" />
DaoCacheKeyGenerator.java
public class DaoCacheKeyGenerator implements CacheKeyGenerator<DaoCacheKey> { @Override public DaoCacheKey generateKey(MethodInvocation methodInvocation) { Class<?> clazz = methodInvocation.getThis().getClass().getInterfaces()[0]; Method method = methodInvocation.getMethod(); String methodName = method.getName(); Class<?>[] parameterClasses = method.getParameterTypes(); return new DaoCacheKey(clazz, methodName, parameterClasses); } @Override public DaoCacheKey generateKey(Object... data) { return null; } }
DaoCacheKey.java
public class DaoCacheKey implements Serializable { private static final long serialVersionUID = 338466521373614710L; private Class<?> clazz; private String methodName; private Class<?>[] parameterClasses; public DaoCacheKey(Class<?> clazz, String methodName, Class<?>[] parameterClasses) { this.clazz = clazz; this.methodName = methodName; this.parameterClasses = parameterClasses; } @Override public boolean equals(Object obj) { // <-- breakpoint if (obj instanceof DaoCacheKey) { DaoCacheKey other = (DaoCacheKey) obj; if (clazz.equals(other.clazz)) { // if @TriggersRemove, reset any cache generated by a find* method of the same DomainDao boolean removeCache = !methodName.startsWith("find") && other.methodName.startsWith("find"); // if @Cacheable, check if the result has been previously cached boolean getOrCreateCache = methodName.equals(other.methodName) && Arrays.deepEquals(parameterClasses, other.parameterClasses); return removeCache || getOrCreateCache; } } return false; } @Override public int hashCode() { // <-- breakpoint return super.hashCode(); } }
上面的问题DaoCacheKey是,该equals方法永远不会被调用(程序永远不会中断),但是hashCode一个方法不会被调用,因此该算法无法被应用。
DaoCacheKey
equals
hashCode
题
有没有人已经管理过这样的缓存?如果是,怎么办?我的尝试是否有意义?如果是,如何使该equals方法代替原来的方法hashCode?通过扩展现有的CacheKeyGenerator?如果是,是哪一个?
这是我最终采用的工作解决方案。精度很低:我的领域都实现了PersistableSpring 的接口。而且,由于我正在使用反射,因此我不确定缓存过程所节省的时间不会减少多少…
Persistable
<ehcache:annotation-driven cache-manager="ehCacheManager" default-cache-key-generator="daoCacheKeyGenerator" /> <bean id="ehCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" /> <bean id="daoCacheKeyGenerator" class="myapp.dao.support.cache.DaoCacheKeyGenerator" />
DaoCacheKeyGenerator.java(使用 gentyref库)
public class DaoCacheKeyGenerator implements CacheKeyGenerator<DaoCacheKey> { @SuppressWarnings("unchecked") @Override public DaoCacheKey generateKey(MethodInvocation methodInvocation) { Method method = methodInvocation.getMethod(); Class<? extends GenericDao<?, ?>> daoType = (Class<? extends GenericDao<?, ?>>) methodInvocation.getThis().getClass().getInterfaces()[0]; Class<? extends Persistable<?>> domainType = getDomainType(daoType); String methodName = method.getName(); Class<?>[] parameterTypes = method.getParameterTypes(); Object[] parameters = methodInvocation.getArguments(); return new DaoCacheKey(domainType, methodName, parameterTypes, parameters); } @SuppressWarnings("unchecked") private Class<? extends Persistable<?>> getDomainType(Class<?> daoType) { Type baseDaoType = GenericTypeReflector.getExactSuperType(daoType, GenericDao.class); ParameterizedType parameterizedBaseDaoType = (ParameterizedType) baseDaoType; return (Class<? extends Persistable<?>>) parameterizedBaseDaoType.getActualTypeArguments()[0]; } @Override public DaoCacheKey generateKey(Object... data) { return null; } }
public class DaoCacheKey implements Serializable { private static final long serialVersionUID = 338466521373614710L; private Class<? extends Persistable<?>> domainType; private String methodName; private Class<?>[] parameterTypes; private Object[] parameters; public DaoCacheKey(Class<? extends Persistable<?>> domainType, String methodName, Class<?>[] parameterTypes, Object[] parameters) { this.domainType = domainType; this.methodName = methodName; this.parameterTypes = parameterTypes; this.parameters = parameters; } public Class<? extends Persistable<?>> getDomainType() { return domainType; } @Override public boolean equals(Object obj) { return this == obj || obj instanceof DaoCacheKey && hashCode() == obj.hashCode(); } @Override public int hashCode() { return Arrays.hashCode(new Object[] { domainType, methodName, Arrays.asList(parameterTypes), Arrays.asList(parameters) }); } }
<cache name="dao" eternal="false" maxElementsInMemory="10000" overflowToDisk="false" timeToIdleSeconds="86400" timeToLiveSeconds="86400" memoryStoreEvictionPolicy="LFU"> <cacheEventListenerFactory class="myapp.dao.support.cache.DaoCacheEventListenerFactory" /> </cache>
DaoCacheEventListenerFactory.java
public class DaoCacheEventListenerFactory extends CacheEventListenerFactory { @Override public CacheEventListener createCacheEventListener(Properties properties) { return new DaoCacheEventListener(); } }
DaoCacheEventListener.java
public class DaoCacheEventListener implements CacheEventListener { @SuppressWarnings("unchecked") @Override public void notifyElementRemoved(Ehcache cache, Element element) throws CacheException { DaoCacheKey daoCachekey = (DaoCacheKey) element.getKey(); List<Class<? extends Persistable<?>>> impacts = getOneToManyImpacts(daoCachekey.getDomainType()); for (DaoCacheKey daoCachedkey : (List<DaoCacheKey>) cache.getKeys()) { if (impacts.contains(daoCachedkey.getDomainType())) { cache.remove(daoCachedkey); } } } @SuppressWarnings("unchecked") private List<Class<? extends Persistable<?>>> getOneToManyImpacts(Class<? extends Persistable<?>> domainType) { List<Class<? extends Persistable<?>>> impacts = new ArrayList<Class<? extends Persistable<?>>>(); impacts.add(domainType); for (Method method : domainType.getDeclaredMethods()) { if (method.isAnnotationPresent(OneToMany.class)) { ParameterizedType parameterizedType = (ParameterizedType) method.getGenericReturnType(); Class<? extends Persistable<?>> impactedDomainType = (Class<? extends Persistable<?>>) parameterizedType.getActualTypeArguments()[0]; if (!impacts.contains(impactedDomainType)) { impacts.addAll(getOneToManyImpacts(impactedDomainType)); } } } return impacts; } @Override public void notifyElementPut(Ehcache cache, Element element) throws CacheException { } @Override public void notifyElementUpdated(Ehcache cache, Element element) throws CacheException { } @Override public void notifyElementExpired(Ehcache cache, Element element) { } @Override public void notifyElementEvicted(Ehcache cache, Element element) { } @Override public void notifyRemoveAll(Ehcache cache) { } @Override public void dispose() { } @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } }
希望能有所帮助 ;)
;)