如何在不使用查询缓存的情况下缓存Spring Data JPA查询方法的结果?

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

 public interface PromotionServiceXrefRepository extends PagingAndSortingRepository { @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 findByCustomerId(int customerId); } 

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

   

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

您拥有的代码不起作用的原因是@Cache不打算以这种方式工作。 如果要缓存查询方法执行的结果,最简单的方法是使用Spring的缓存抽象 。

 interface PromotionServiceXrefRepository extends PagingAndSortingRepository { @Query("…") @Cacheable("servicesByCustomerId") Set findByCustomerId(int customerId); @Override @CacheEvict(value = "servicesByCustomerId", key = "#p0.customer.id")  S save(S service); } 

此设置将导致客户标识符缓存对findByCustomerId(…)的调用结果。 注意,我们在重写的save(…)方法中添加了@CacheEvict ,这样每当保存实体时,我们就会清除用查询方法填充的缓存。 这可能也必须传播到delete(…)方法。

现在,您可以继续配置专用的CacheManager (请参阅参考文档以获取详细信息),以插入您喜欢的缓存解决方案(在此处使用简单的ConcurrentHashMap )。

  @Configuration @EnableCaching class CachingConfig { @Bean CacheManager cacheManager() { SimpleCacheManager cacheManager = new SimpleCacheManager(); cacheManager.addCaches(Arrays.asList(new ConcurrentMapCache("servicesByCustomerId))); return cacheManager; } } 

你需要注意的是,通过放弃Hibernate QueryCache,你有责任在保存,更新,删除影响查询结果的实体(Oliver通过设置CacheEvict保存时所做的事情)时使失效的查询失效 – 我认为可能是一种痛苦 – 或者至少你需要考虑并忽略它,如果它对你的场景来说不是真正的问题。

首先我引用你的问题:

我究竟做错了什么?

您尝试命名缓存的方式不适合 hibernate如何使用它。 检查使用org.hibernate.internal.CacheImpl ,它基于:

 if ( settings.isQueryCacheEnabled() ) { final TimestampsRegion timestampsRegion = regionFactory.buildTimestampsRegion( qualifyRegionName( UpdateTimestampsCache.REGION_NAME ), sessionFactory.getProperties() ); updateTimestampsCache = new UpdateTimestampsCache( sessionFactory, timestampsRegion ); ... } 

并且UpdateTimestampsCache.REGION_NAME (等于org.hibernate.cache.spi.UpdateTimestampsCache )是您缺少的缓存名称 。 对于查询缓存,您必须使用该缓存名称而不是其他名称!

现在很少有与您的问题相关的其他想法:

  • 删除@Cache并将缓存名称设置为org.hibernate.cache.spi.UpdateTimestampsCache将允许您的查询通过hibernate使用ehcache进行缓存(此处不涉及spring cache abstraction)
  • 设置一个硬编码的缓存名称不会让你开心我很确定,但至少你知道为什么会这样
  • Balamaci Serban(上面的post)非常痛苦

下面是我的一个项目的配置,其中ehcache + @Query + @QueryHints 按预期工作ehcache/ehcache-in-memory.xml文件):

         

和hibernate.properties:

 hibernate.jdbc.batch_size=20 hibernate.show_sql=true hibernate.format_sql=true hibernate.validator.autoregister_listeners=false hibernate.cache.use_second_level_cache=true hibernate.cache.use_query_cache=true hibernate.cache.region.factory_class=org.hibernate.cache.ehcache.EhCacheRegionFactory hibernate.hbm2ddl.auto=update net.sf.ehcache.configurationResourceName=ehcache/ehcache-in-memory.xml hibernate.dialect=org.hibernate.dialect.H2Dialect 

以及我的解释适用的pom.xml中的一些版本:

 5.0.6.RELEASE 5.0.5.RELEASE 2.1.0.RELEASE 5.2.13.Final 2.9.4 

完整的工作测试是image.persistence.repositories.ImageRepositoryTest.java,可在此处找到: https : //github.com/adrhc/photos-server/tree/how-to-cache-results-of-a-spring-data- JPA的查询方法,而无需-使用查询缓存
是的,如果你真的想使用我的shell脚本,请运行mvn clean install或更改我的env.sh 然后检查代表3x imageRepository.count()调用的sql查询数。