Spring Data JPA(hibernateバックエンド)リポジトリクラスを使用したSpring Bootアプリがあります。いくつかのカスタムFinderメソッドを追加しました。特定の@Query
注釈を使用して、データの取得方法を指定します。すでに休止状態の2次キャッシュ用にEhCacheをセットアップしましたが、これまでのところ、これらの結果のキャッシュを取得できる唯一の方法は、休止状態のクエリキャッシュを有効にすることです。特定のキャッシュを定義し、実際のドメインオブジェクトを通常のFinderのようにそこに保存したいと思います。以下は私のレポコードです:
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はクエリを実行しません。ただし、クエリのキャッシュを無効にすると、キャッシュされません。ここで何が間違っていますか?助けてください!
使用しているコードが機能しないのは、_@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
(詳細は 参照ドキュメント を参照)を構成して、好みのキャッシングソリューションをプラグインすることができます(ここではプレーンConcurrentHashMap
を使用) 。
_ @Configuration
@EnableCaching
class CachingConfig {
@Bean
CacheManager cacheManager() {
SimpleCacheManager cacheManager = new SimpleCacheManager();
cacheManager.addCaches(Arrays.asList(new ConcurrentMapCache("servicesByCustomerId)));
return cacheManager;
}
}
_
Hibernate QueryCacheを放棄することにより、クエリ結果に影響を与えるエンティティを保存、更新、削除するときに失効するクエリを無効にする責任があることに注意する必要があります(オリバーが保存時にCacheEvictを設定することで行うこと)苦痛になる可能性があります。少なくとも、シナリオに問題がない場合は、考慮して無視する必要があります。
最初にあなたの質問を引用します:
私は何を間違えていますか?
あなたがしようとしている方法はnameキャッシュは適切ではない hibernateがキャッシュを使用する方法です。以下に基づく_org.hibernate.engine.spi.CacheInitiator
_を使用する_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
_と等しい)は、キャッシュ名として欠落しているものです。 クエリキャッシュには、exactlyそのキャッシュ名を使用する必要があります。
問題に関連する他のいくつかの考え:
@Cache
_を削除し、キャッシュ名を_org.hibernate.cache.spi.UpdateTimestampsCache
_に設定すると、クエリがhibernateによってehcacheでキャッシュできるようになります(ここではスプリングキャッシュの抽象化は関係ありません)以下は、ehcache + @Query + @QueryHintsが期待通りに動作する私のプロジェクトの1つからの構成です(_ehcache/ehcache-in-memory.xml
_ file):
_<?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>
_
および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からのいくつかのバージョン:
_<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>
_
そして、完全な動作テストはimage.persistence.repositories.ImageRepositoryTest.Javaにあります: https://github.com/adrhc/photos-server/tree/how-to-cache -spring-data-jpa-query-method-without-using-query-cacheの結果
はい、本当にシェルスクリプトを使用する場合は、_mvn clean install
_を実行するか、_env.sh
_を変更します。 3x imageRepository.count()
呼び出しに代わってSQLクエリの数を確認します。