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 ;)