尝试使用Spring使用EhCache和扩展Hibernate的JpaRepository的自定义GenericDao接口 [英] Trying to use EhCache using Spring and a custom GenericDao interface that extends the Hibernate's JpaRepository
问题描述
以下是我的工作(简化版) GenericDao
界面,其中由任何 DomainDao
执行:
GenericDao.java
@NoRepositoryBean
public interface GenericDao< E extends Persistable< K>,K extends Serializable>扩展JpaRepository< E,K> {
public List< E>找到所有();
public E persist(E entity);
$ b
GenericDaoImpl.java p>
public class GenericDaoImpl< E extends Persistable< K>,K extends Serializable>扩展SimpleJpaRepository< E,K>实现GenericDao< E,K> {
private final JpaEntityInformation< E,?> entityInformation;
private final EntityManager em;
private final Class< E>类型;
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();
$ b @Override
@Transactional
public E persist(E entity){
if(entityInformation.isNew(entity)||!EntityUtils。 isPrimaryKeyGenerated(type)&&!em.contains(entity)){
em.persist(entity);
}
返回实体;
}
}
例如,要管理域 Foo
和 Bar
,您只需要创建两个界面:
FooDao.java
$ b
public interface FooDao extends GenericDao< Foo,Integer> {
}
BarDao.java
public interface BarDao extends GenericDao< Bar,Integer> {
}
@Autowired $ c $使用良好的实体和主键类型,
的注释将自动实例化 GenericDaoImpl
。
问题 m现在试图在我的DAO上添加一个缓存过程,使用EhCache和 EhCache Spring Annotations model。 GenericDao.java applicationContext.xml p> ehcache.xml 使用 strong> Trails
@NoRepositoryBean
public interface GenericDao< E extends Persistable< K>,K extends Serializable>扩展JpaRepository< E,K> {
@Cacheable(cacheName =dao)
public List< E>找到所有();
@TriggersRemove(cacheName =dao)
public E persist(E entity);
$ b
< ehcache:annotation-driven cache-manager =ehCacheManager/>
< bean id =ehCacheManagerclass =org.springframework.cache.ehcache.EhCacheManagerFactoryBean/>
< cache name =dao
eternal =false
maxElementsInMemory =10000
overflowToDisk =false
timeToIdleSeconds =86400
timeToLiveSeconds =86400
memoryStoreEvictionPolicy =LFU/>
GenericDao
是否缓存应该彼此独立管理每个 DomainDao
。例如,使用当前配置,如果我调用 fooDao.findAll()
,然后 fooDao.findAll()
生成的缓存将被重置,因为将使用相同的缓存(即< cache
我试图实现自己的 CacheKeyGenerator
,它将考虑调用 DomainDao
$ b
applicationContext.xml
< ehcache:annotation-driven cache-manager =ehCacheManagerdefault-cache-key-generator =daoCacheKeyGenerator/>
< bean id =ehCacheManagerclass =org.springframework.cache.ehcache.EhCacheManagerFactoryBean/>
< bean id =daoCacheKeyGeneratorclass =myapp.dao.support.DaoCacheKeyGenerator/>
DaoCacheKeyGenerator.java
public class DaoCacheKeyGenerator实现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();
返回新的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其他=(DaoCacheKey)obj;
if(clazz.equals(other.clazz)){
// if @TriggersRemove,重置由同一DomainDao的$ find方法生成的缓存
boolean removeCache =!methodName.startsWith (查找)&& other.methodName.startsWith( 找到);
// if @Cacheable,检查结果是否先前已被缓存
boolean getOrCreateCache = methodName.equals(other.methodName)&& Arrays.deepEquals(parameterClasses,other.parameterClasses);
返回removeCache || getOrCreateCache;
}
}
返回false;
$ b $ @Override
public int hashCode(){//< - breakpoint
return super.hashCode();
}
}
上述 DaoCacheKey
,就是等于
方法永远不会被调用(程序永远不会中断),但 hashCode
这样做,所以算法无法应用。
问题
有人已经管理过这样的缓存吗?如果是的话如何?我的尝试是否相关?如果是,如何让等于
方法被调用,而不是 hashCode
一个?通过扩展现有的 CacheKeyGenerator
?如果是的话,哪一个?
这是我最终采纳的工作解决方案。只有少数精度:我的域名全部实施 Persistable
。此外,由于我使用了反射,因此我不确定缓存过程所节省的时间是否会减少......
applicationContext .xml
$ b
< ehcache:annotation-driven cache-manager =ehCacheManager default-cache-key-generator =daoCacheKeyGenerator/>
< bean id =ehCacheManagerclass =org.springframework.cache.ehcache.EhCacheManagerFactoryBean/>
< bean id =daoCacheKeyGeneratorclass =myapp.dao.support.cache.DaoCacheKeyGenerator/>
public class DaoCacheKeyGenerator实现CacheKeyGenerator< DaoCacheKey> {
@SuppressWarnings(unchecked)
@Override
public DaoCacheKey generateKey(MethodInvocation methodInvocation){
Method method = methodInvocation.getMethod();
Class <?扩展GenericDao<?,?>>> daoType =(Class< ;? extends GenericDao<?,?>>)methodInvocation.getThis()。getClass()。getInterfaces()[0];
Class <?延伸Persistable<>> domainType = getDomainType(daoType);
String methodName = method.getName();
Class<?> [] parameterTypes = method.getParameterTypes();
Object [] parameters = methodInvocation.getArguments();
返回新的DaoCacheKey(domainType,methodName,parameterTypes,parameters);
}
@SuppressWarnings(unchecked)
private class <?延伸Persistable<>> getDomainType(Class<?> daoType){
类型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;
}
}
DaoCacheKey.java
public class DaoCacheKey implements Serializable {
$ b $ private static最终长期serialVersionUID = 338466521373614710L;
私人课程<?延伸Persistable<>> domainType;
private String methodName;
private Class<?> [] parameterTypes;
private Object []参数;
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 <?延伸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)});
}
}
ehcache.xml
$ b
< 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
$ b $
public class DaoCacheEventListenerFactory extends CacheEventListenerFactory {
$ b @Override
public CacheEventListener createCacheEventListener(Properties properties){
返回新的DaoCacheEventListener();
}
}
DaoCacheEventListener.java
public class DaoCacheEventListener implements CacheEventListener {
@SuppressWarnings (unchecked)
@Override
public void notifyElementRemoved(Ehcache cache,Element element)抛出CacheException {
DaoCacheKey daoCachekey =(DaoCacheKey)element.getKey();
列表< Class< ;?延伸Persistable<>>> impact = getOneToManyImpacts(daoCachekey.getDomainType()); (DaoCacheKey daoCachedkey:(List< DaoCacheKey>)cache.getKeys()){
if(impact.contains(daoCachedkey.getDomainType())){
cache.remove(daoCachedkey) ;
$ b $ @SuppressWarnings(unchecked)
private List< Class< ;?延伸Persistable<>>> getOneToManyImpacts(Class< ;? extends Persistable<>> domainType){
List< Class< ;?延伸Persistable<>>> impact = new ArrayList< Class< ;?延伸Persistable<>>>();
impact.add(domainType); (方法方法:domainType.getDeclaredMethods()){
if(method.isAnnotationPresent(OneToMany.class)){
ParameterizedType parameterizedType =(ParameterizedType)method.getGenericReturnType();
Class <?延伸Persistable<>> impactedDomainType =(Class< ;? extends Persistable<>)parameterizedType.getActualTypeArguments()[0];
if(!impact.contains(impactedDomainType)){
impact.addAll(getOneToManyImpacts(impactedDomainType));
}
}
}
返回影响;
$ b @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){
}
$ b @覆盖
public void notifyElementEvicted(Ehcache缓存,Element元素){
}
$ b @覆盖
public void notifyRemoveAll(Ehcache缓存) {
}
@Override
public void dispose(){
}
$ b $ @Override
public Object clone()抛出CloneNotSupportedException {
返回super.clone();
}
}
希望可以帮助;)
Background
Here is my working (simplified) GenericDao
interface, which is implemented by any 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;
}
}
For example, to manage the domains Foo
and Bar
, you just need to create two interfaces as follow:
FooDao.java
public interface FooDao extends GenericDao<Foo, Integer> {
}
BarDao.java
public interface BarDao extends GenericDao<Bar, Integer> {
}
The @Autowired
annotation of Spring
will automatically instantiate a GenericDaoImpl
with the good entity and primary key types.
Problem
I'm now trying to add a caching process on my DAOs, using EhCache and the EhCache Spring Annotations model.
GenericDao.java
@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" />
The problem with the use of a GenericDao
, is that the cache should manage each DomainDao
independently of each other. For example, with the current configuration, if I call fooDao.findAll()
, and then barDao.persist(new Bar())
, the cache generated by fooDao.findAll()
will be reset, since the same cache would have been used (i.e. <cache name="dao" />
), while it shouldn't.
Trails
I tried to implement my own CacheKeyGenerator
, that will take into account the type of the calling DomainDao
:
applicationContext.xml
<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();
}
}
The problem with the above DaoCacheKey
, is that the equals
method get never called (the program never breaks at least), but the hashCode
one does, so the algorithm can't get applied.
Question
Has anyone already managed such a cache? If yes how? Does my try is relevant? If yes, how to make the equals
method called, instead of the hashCode
one? By extending an existing CacheKeyGenerator
? If yes, which one?
Here is the working solution I finally adopted. Just few precisions: my domains all implement the Persistable
interface of Spring. Moreover, since I'm using reflection, I'm not sure the time saved by the caching process won't be a bit reduced...
applicationContext.xml
<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 (using the gentyref library)
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;
}
}
DaoCacheKey.java
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) });
}
}
ehcache.xml
<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();
}
}
Hope that could help ;)
这篇关于尝试使用Spring使用EhCache和扩展Hibernate的JpaRepository的自定义GenericDao接口的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!