我有一个带有Spring Data JPA(hibernate后端)存储库类的Spring Boot应用程序。我添加了一些自定义查找程序方法,其中一些方法具有特定的@Query
注释,以告诉它如何获取数据。我已经为hibernate二级缓存设置了EhCache,但到目前为止,我可以获得这些结果缓存的唯一方法是启用hibernate查询缓存。我更喜欢定义一个特定的缓存并将实际的域对象存储在那里,就像它是一个普通的查找程序一样。以下是我的回购代码:
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);
}
这是我定义的“promoteServiceXrefByCustomerId”缓存,它没有被使用:
<cache name="promotionServiceXrefByCustomerId" overflowToDisk="true" diskPersistent="true"
maxEntriesLocalHeap="3000000" eternal="true" diskSpoolBufferSizeMB="20" memoryStoreEvictionPolicy="LFU"
transactionalMode="off" statistics="true">
</cache>
我做错了什么?如果我启用StandardQueryCache
,则此数据会缓存在那里,而hibernate不会执行查询。但是当我禁用查询缓存时,这不会被缓存。我在这做错了什么?请帮助!
答案 0 :(得分:48)
您拥有的代码不起作用的原因是@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(…)
的调用结果。请注意,我们在重写的@CacheEvict
方法中添加了save(…)
,以便每当保存实体时,我们会使用查询方法填充的缓存被逐出。这可能也必须传播到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;
}
}
答案 1 :(得分:12)
您需要注意的是,放弃Hibernate QueryCache,您负责在保存,更新,删除影响查询结果的实体时使失效的查询失效(Oliver通过在保存时设置CacheEvict来做什么) - 我认为这可能是一种痛苦 - 或者至少你需要考虑并忽略它,如果它对你的场景不是真正的问题。
答案 2 :(得分:3)
首先,我引用您的问题:
我在做什么错?
您尝试为缓存命名 的方式与休眠将如何使用 不符。选中使用基于{1>的org.hibernate.engine.spi.CacheInitiator
的{{1}}
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
将允许您使用 hibernate 使用ehcache缓存查询(此处不涉及spring缓存抽象)下面是我的一个项目中的配置,其中ehcache + @Query + @QueryHints 正常工作(org.hibernate.cache.spi.UpdateTimestampsCache
文件):
ehcache/ehcache-in-memory.xml
和hibernate.properties:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache name="in-memory" xmlns="http://ehcache.org/ehcache.xsd">
<!--<diskStore path="java.io.tmpdir"/>-->
<!--
30d = 3600×24×30 = 2592000
-->
<cache name="org.hibernate.cache.internal.StandardQueryCache"
maxElementsInMemory="9999" eternal="false"
timeToIdleSeconds="2592000" timeToLiveSeconds="2592000"
overflowToDisk="false" overflowToOffHeap="false"/>
<cache name="org.hibernate.cache.spi.UpdateTimestampsCache"
maxElementsInMemory="9999" eternal="true"
overflowToDisk="false" overflowToOffHeap="false"/>
<defaultCache maxElementsInMemory="9999" eternal="false"
timeToIdleSeconds="2592000" timeToLiveSeconds="2592000"
overflowToDisk="false" overflowToOffHeap="false"/>
</ehcache>
和适用于我的解释的pom.xml中的某些版本:
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
完整的工作测试是在以下位置找到的 image.persistence.repositories.ImageRepositoryTest.java :
https://github.com/adrhc/photos-server/tree/how-to-cache-results-of-a-spring-data-jpa-query-method-without-using-query-cache
是的,如果您确实想使用我的shell脚本,请运行<springframework.version>5.0.6.RELEASE</springframework.version>
<spring-security.version>5.0.5.RELEASE</spring-security.version>
<spring-data-jpa.version>2.1.0.RELEASE</spring-data-jpa.version>
<hibernate.version>5.2.13.Final</hibernate.version>
<jackson-datatype-hibernate5.version>2.9.4</jackson-datatype-hibernate5.version>
或更改我的mvn clean install
。然后代表3x env.sh
调用检查sql查询的数量。