如何在Spring Data JPA CRUDRepository中添加缓存功能

19
我希望在findOne方法中添加“Cacheable”注释,并在删除或发生方法时清除缓存。

我该怎么做?

4个回答

13
virsir,如果您使用Spring Data JPA(仅使用接口),还有一种方法。这是我所做的,为相似结构的实体生成通用dao:
public interface CachingDao<T, ID extends Serializable> extends JpaRepository<T, ID>, JpaSpecificationExecutor<T> {

@Cacheable(value = "myCache")
T findOne(ID id);

@Cacheable(value = "myCache")
List<T> findAll();

@Cacheable(value = "myCache")
Page<T> findAll(Pageable pageable);

....

@CacheEvict(value = "myCache", allEntries = true)
<S extends T> S save(S entity);

....

@CacheEvict(value = "myCache", allEntries = true)
void delete(ID id);
}

2
请问您为什么在save方法中使用了@CacheEvict?为什么不使用@CachePut呢?保存操作不会正确更新缓存,因此在保存后(假设缓存仍然存在)不需要运行@Cacheable吗? - taylorcressy
2
@seven 不错!请注意,可以使用@CacheConfig注释Repo,以便将cache名称放在CachingDao之外。 - oak
1
继@delver的评论之后,建议您只使用@Cache*注释具体类(和具体类的方法),而不是注释接口。有关更多详细信息,请参见源--http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html - jzonthemtn
1
奇怪的是,Spring 的示例中展示了在接口上使用 @Cache* 注解... https://github.com/spring-projects/spring-data-examples/blob/master/jpa/example/src/main/java/example/springdata/jpa/caching/CachingUserRepository.java - Snekse
有人知道为什么在保存或删除单个记录时需要驱逐所有条目吗? - Snekse
@seven - 有没有办法编写一些代码来更新由Spring管理的缓存? 我想在JMS消费者实现中更新缓存。 - Andy Dufresne

9

我认为@seven的回答基本上是正确的,但还有2个要点遗漏:

  1. We cannot define a generic interface, I'm afraid we have to declare every concrete interface separately since annotation cannot be inherited and we need to have different cache names for each repository.

  2. save and delete should be CachePut, and findAll should be both Cacheable and CacheEvict

    public interface CacheRepository extends CrudRepository<T, String> {
    
        @Cacheable("cacheName")
        T findOne(String name);
    
        @Cacheable("cacheName")
        @CacheEvict(value = "cacheName", allEntries = true)
        Iterable<T> findAll();
    
        @Override
        @CachePut("cacheName")
        T save(T entity);
    
        @Override
        @CacheEvict("cacheName")
        void delete(String name);
    }
    

Reference


我不知道为什么会有负面评价,但是1)是正确的,我猜evict allentries在那里是因为你的缓存可能具有与PK不同的键值,因此在修改记录时,您应该指定要清除什么 - 如果您只有PK,则不知道缓存键,因此您不知道哪个缓存记录无效。 - Cipous

5

我是这样解决的,并且它正常工作:


public interface BookRepositoryCustom {

    Book findOne(Long id);

}

public class BookRepositoryImpl extends SimpleJpaRepository<Book,Long> implements BookRepositoryCustom {

    @Inject
    public BookRepositoryImpl(EntityManager entityManager) {
        super(Book.class, entityManager);
    }

    @Cacheable(value = "books", key = "#id")
    public Book findOne(Long id) {
        return super.findOne(id);
    }

}

public interface BookRepository extends JpaRepository<Book,Long>, BookRepositoryCustom {

}

我有许多代码库,有没有简便的方法来实现这个? - virsir
我不知道其他的方法,我只知道这一种方法。 - Sujeet

2
尝试按照此处所述提供MyCRUDRepository(一个接口和一个实现):向所有存储库添加自定义行为。然后,您可以覆盖并添加这些方法的注释:
findOne(ID id)
delete(T entity)
delete(Iterable<? extends T> entities)
deleteAll() 
delete(ID id) 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接