2014-10-07 267 views
11

我有Spring数据JPA(hibernate backend)存储库类的Spring Boot应用程序。我添加了一些自定义查找方法,其中一些用特定的@Query注释来告诉它如何获取数据。我已经为hibernate二级缓存设置了EhCache,但到目前为止,我能够获得这些结果缓存的唯一方法是启用hibernate查询缓存。我宁愿定义一个特定的缓存并将实际的域对象存储在那里,就好像它是一个普通的查找器。下面是我的回购代码:如何在不使用查询缓存的情况下缓存Spring Data JPA查询方法的结果?

public interface PromotionServiceXrefRepository extends PagingAndSortingRepository<PromotionServiceXref, Integer> { 

    @Query("SELECT psx FROM Customer c " + 
     "JOIN c.customerProductPromotions cpp " + 
     "JOIN cpp.productPromotion pp " + 
     "JOIN pp.promotion p JOIN p.promotionServiceXrefs psx " + 
     "WHERE c.customerId = ?1") 
    @QueryHints(@QueryHint(name = "org.hibernate.cacheable", value = "true")) 
    @Cache(usage = CacheConcurrencyStrategy.READ_ONLY, region = "promotionServiceXrefByCustomerId") 
    Set<PromotionServiceXref> findByCustomerId(int customerId); 
} 

这里是我所定义的“promotionServiceXrefByCustomerId”缓存,即没有被使用:

<cache name="promotionServiceXrefByCustomerId" overflowToDisk="true" diskPersistent="true" 
     maxEntriesLocalHeap="3000000" eternal="true" diskSpoolBufferSizeMB="20" memoryStoreEvictionPolicy="LFU" 
     transactionalMode="off" statistics="true"> 
</cache> 

我在做什么错?如果我启用StandardQueryCache,那么这些数据会被缓存到那里,而hibernate不会执行查询。但是,当我禁用查询缓存时,这不会被缓存。我在这里做错了什么?请帮忙!

+0

为什么'@Cache'注解对非实体做任何事情?该注释的意思是不在任何类或接口上的实体。 – 2014-10-07 18:50:23

+0

我想弄清楚...所以任何帮助将不胜感激。 Spring的运行时创建的PagingAndSortingRepository中指定的另一个find ...方法使用JPA/Hibernate的二级缓存提供缓存。这工作正常。但是我创建的这个查找方法,我无法弄清楚如何让IT缓存...... – 2014-10-07 19:05:08

+1

我怀疑'findAll'是否缓存,除非你在你的实体上存放了注释(这是他们应该去的地方)。如果您尝试(或期待)要缓存的非缓存实体,则它将无法工作,只有查询缓存将在此情况下起作用。 – 2014-10-08 05:29:56

回答

35

您所拥有的代码不能正常工作的原因是@Cache不适用于这种方式。如果要缓存查询方法执行的结果,最简单的方法是使用Spring的caching abstraction

interface PromotionServiceXrefRepository extends PagingAndSortingRepository<PromotionServiceXref, Integer> { 

    @Query("…") 
    @Cacheable("servicesByCustomerId") 
    Set<PromotionServiceXref> findByCustomerId(int customerId); 

    @Override 
    @CacheEvict(value = "servicesByCustomerId", key = "#p0.customer.id") 
    <S extends PromotionServiceXref> S save(S service); 
} 

该设置将导致调用findByCustomerId(…)的调用结果被客户标识符缓存。请注意,我们为覆盖的save(…)方法添加了@CacheEvict,这样,只要保存实体,我们使用查询方法填充的缓存就会被逐出。这可能也必须传播到delete(…)方法。

现在您可以继续配置专用CacheManager(请参阅reference documentation以了解详细信息)以插入您喜欢的任何缓存解决方案(在此处使用普通的ConcurrentHashMap)。

@Configuration 
@EnableCaching 
class CachingConfig { 

    @Bean 
    CacheManager cacheManager() { 

    SimpleCacheManager cacheManager = new SimpleCacheManager(); 
    cacheManager.addCaches(Arrays.asList(new ConcurrentMapCache("servicesByCustomerId))); 

    return cacheManager; 
    } 
} 
+0

谢谢。我确实开始走这条路,但遇到了一个问题,当我在一个类中有多个方法时,我需要缓存抽象,但是实际上是从同一个类中的另一个方法调用的。我将需要去AspectJ路线。事实证明,我已经放弃了为了商业目的而缓存这些数据的需要,所以这个“问题”不再是一个问题。但是你是对的,最好的路线肯定是转向Spring的Cache抽象。 谢谢! – 2014-10-10 17:13:27

+6

如果我们将实体缓存在Spring Cache中,是不是会导致所有类型的问题和奇怪的行为? – 2016-01-13 10:01:59

+0

Spring建议只注解具体的类(http://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html段落在“方法可见性和缓存注释”之后),我碰到此错误在接口上使用它:“无法生成类[com.sun.proxy。$ Proxy180]类的CGLIB子类”。处理这种情况的推荐方法是什么? (这是Spring 4.1.7) – dave 2017-02-13 10:50:09

7

你需要意识到,通过对Hibernate的实现QueryCache放弃你是负责保存时,更新,删除那些影响了查询结果实体变得陈旧查询无效(什么奥利弗是通过设置CacheEvict做在保存中) - 我认为这可能是一种痛苦 - 或者至少您需要考虑并忽略它,如果它对您的情况不是真正的问题。