如何在不使用查询缓存的情况下缓存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查询数。
- 指定全局EhCache容量
- java.lang.NullPointerException Hibernate与Ehcache一起使用
- 多个线程可以使用相同CacheManager的相同Ehcache对象吗?
- java.lang.NoClassDefFoundError:org / hibernate / cache / EntityRegion配置EHCache
- 写后面的缓存Ehcache新function?
- ehcache在同一主机上的2个实例之间自动发现(通过多播)
- 如何为Spring Cache设置自定义KeyGenerator?
- 将EhCache磁盘库内容加载到内存中
- Spring:hibernate + ehcache