当前位置: 首页 > 知识库问答 >
问题:

尝试使用Spring和自定义GenericDao接口来使用EhCache,该接口扩展了Hibernate的JpaRepository

孙泳
2023-03-14

背景

这是我的工作(简化)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;
    }

}

例如,要管理域FooBar,您只需要创建两个接口,如下所示:

FooDao.java

public interface FooDao extends GenericDao<Foo, Integer> {

}

巴尔道.java

public interface BarDao extends GenericDao<Bar, Integer> {

}

Spring@Autowired注释将自动实例化具有良好实体和主键类型的GenericDaoImpl

问题

我现在正尝试使用EhCache和EhCache-Spring注释模型在DAO上添加缓存过程。

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

}

应用上下文.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" />

使用< code>GenericDao的问题是,缓存应该相互独立地管理每个< code>DomainDao。例如,在当前配置下,如果我调用< code>fooDao.findAll(),然后调用< code > bardao . persist(new Bar()),则由< code>fooDao.findAll()生成的缓存将被重置,因为会使用相同的缓存(即< code >

创新

我试图实现我自己的CacheKeyGenerator,这将考虑调用DomainDao的类型:

应用上下文.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();
    }

}

上述 DaoCacheKey 的问题在于,equals 方法永远不会被调用(程序至少永远不会中断),但 hashCode 会这样做,因此算法无法应用。

问题

有人管理过这样的缓存吗?如果是,怎么做?我的尝试有意义吗?如果是,如何调用< code>equals方法,而不是< code>hashCode方法?通过扩展现有的< code>CacheKeyGenerator?如果有,是哪一个?

共有1个答案

怀德馨
2023-03-14

以下是我最终采用的工作方案。只有几个精确:我的域都实现了Spring的< code>Persistable接口。此外,由于我使用了反射,我不确定缓存过程节省的时间会不会减少一点...

应用上下文.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(使用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;
    }

}

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

}

希望能对<代码>有所帮助;)

 类似资料:
  • 问题内容: 背景 这是我的工作(简化)界面,可通过任何方式实现: GenericDao.java GenericDaoImpl.java 例如,要管理和,只需创建两个接口,如下所示: FooDao.java BarDao.java 的注释将自动使用良好的实体和主键类型实例化一个。 问题 我现在正在尝试使用EhCache和EhCache Spring Annotations 模型在DAO上添加一个缓

  • 我正在编写一个代码,它取两个点,确定两个点的大小,比较它们,并返回哪个更大。我有我的主类和方法来运行所有的东西,然后还有另一个类点来实现我的接口。但是,我无法使从接口调用的方法正常工作。我的代码是: 当我尝试运行它时,我得到错误“exception in thread”main“java.lang.nullPointerException:不能调用”point.getMetality()“,因为”

  • 问题内容: 考虑以下代码: 由于类型擦除的限制,我收到以下错误消息: java.lang.Comparable的不能使用不同的参数继承:和 我有以下要求: 需要是一个枚举,因为我需要将其用作注释中的默认值。 我的界面的约定是,它必须具有可比性。 我已经尝试在接口中使用通用范围,但是Java不支持此功能。 问题答案: 枚举实现Comparable,因此FooImpl最终使用不兼容的参数将Compar

  • 本文向大家介绍tk.mybatis扩展通用接口使用详解,包括了tk.mybatis扩展通用接口使用详解的使用技巧和注意事项,需要的朋友参考一下  一.tk.mybatis已经为我们封装好了许多拆箱即用的通用mapper,但在实际的项目开发中想必不少小伙伴在数据库设计中都会采用逻辑删除这种方案,再去使用通用的mapper接口就不行了。 这时候就需要我们封装一些扩展的通用Mapper接口。 二.项目中

  • 首先定义一个特别的实体Repository interface。接口必须继承自Repository并且要定义为实体类和一个ID类型。如果你想要为这个实体类实现CRUD操作,继承CrudRepository替换Repository

  • 问题内容: 我必须实现一个RMI服务器,该服务器将成为其他两个RMI服务的前端。因此,我决定逻辑上要做的是使该接口实现其他两个服务的接口。 但是,StudentServer上有一种方法 我也想抛出一个错误,因为该服务还将在尝试添加详细信息之前验证该书是否确实存在。 这是可能的还是我的设计思想完全被取消了,实际上这是一个糟糕的设计思想,好像其他接口都已更改一样?而且,为里面的所有方法编写方法签名会更