14

私のアプリケーションでは、JPA (1.2)、Spring (3.1.2)、Spring Data (1.1.0)、および Hibernate (4.1.7) を使用しています。データベース: Oracle10g

2 番目のレベルのキャッシュを有効にしました。エンティティでは問題なく動作していますが、名前付きクエリのキャッシュで問題が発生しています。

問題は次のとおりです。名前付きクエリに同じwhere句があり、selectステートメントが異なる場合、最初のクエリが実行されても、2番目のクエリでも同じ結果が得られます。

私の最初のクエリ(countRelease)のように

select count(r) from Release r where r.type in 
(select c.contentTypeId from ContentType c where c.parentContentTypeId is NULL)
order by r.validityStart

そして2番目のクエリ(findRelease)は

select r from Release r where r.type in 
(select c.contentTypeId from ContentType c where c.parentContentTypeId is NULL)   
order by r.validityStart

最初のクエリが最初に実行された場合、カウントが発生し、その後、2 番目のクエリを実行した場合もカウントが発生し、リリース エンティティのリストが表示されます。

クエリ キャッシュを削除すると正常に動作し、2 番目のクエリの where 句を変更すると正常に動作しますが、それを行う必要はありません。

この問題をどのように解決できますか?

私のJavaコード

@Query(name="findRelease")
@QueryHints({@QueryHint(name = "org.hibernate.cacheRegion", value ="cvodrelease"),@QueryHint(name = "org.hibernate.cacheable", value ="true") })
public List<Release> findRelease();

@Query(name="countRelease")
@QueryHints({@QueryHint(name = "org.hibernate.cacheRegion", value ="cvodrelease"),@QueryHint(name = "org.hibernate.cacheable", value ="true") })
public Long  countOfRelease(Date today);

キャッシュ構成

<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.EhCacheRegionFactory"/>
<property name="hibernate.cache.use_query_cache" value="true"/>
<property name="hibernate.cache.use_second_level_cache" value="true"/>
<property name="hibernate.cache.provider_class" value="net.sf.ehcache.hibernate.EhCacheProvider" /> 
<property name="hibernate.cache.provider_configuration_file_resource_path" value="ehcache.xml" />

<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager"  p:cacheManager-ref="ehcache"/>

<bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:configLocation="ehcache.xml"  p:shared="true"/> 
4

3 に答える 3

3

JPA 1.0 標準にはキャッシングが含まれていませんでした (JPA 1.2 は存在しません)。

JPA 2.0 標準では、キャッシングが導入されました。これには、共有キャッシュ (各 EntityManagerFactory インスタンスの「第 1 レベルのキャッシュ」) とアプリケーション キャッシュ (すべての EntityManagerFactor インスタンスの第 2 レベルのキャッシュ) が含まれます。また、各 EntityManager インスタンスの各 PersistenceContext は、独自の最低レベルのキャッシュ (「ゼロ レベル キャッシュ」) として機能します。

これが意味することは、あなたの振る舞いはすべて Hibernate 4.1.7 に固有のものであり、標準や他の製品とは何の関係もないということです。

データ キャッシュにクエリ結果の ID のキャッシュ データがない場合、キャッシュは使用されません。

これは、Hibernate や JPA 仕様ではなく、Apache OpenJPA ドキュメントからの直接の引用です。無視できますが、Hibernate には当てはまるようです。

カスタム フィールド タイプまたは BigDecimal または BigInteger フィールドのプロジェクションをもたらすクエリはキャッシュされません。

これは、Hibernate や JPA 仕様ではなく、Oracle Kodo JPA ドキュメントからの直接の引用です。これは無視するのが賢明かもしれません。

クエリ キャッシュは、キャッシュ内の実際のエンティティの状態をキャッシュしません。識別子の値と値の型の結果をキャッシュします。したがって、クエリ結果キャッシュの一部としてキャッシュする必要があるエンティティに対しては、常にクエリ キャッシュを 2 次キャッシュと組み合わせて使用​​してください。.

これは、Hibernate 4.1 ドキュメントからの直接の引用です。したがって、このアドバイスに従うことができます - コンテキストを理解する限り: クエリから返されたエンティティをキャッシュしたい場合は、第 2 レベルのキャッシュを含めるように言っています。エンティティ オブジェクト全体をキャッシュするのではなく、プリミティブ データ型 (プロジェクション) を含む NamedQueries の結果をキャッシュするだけの場合は、最初のレベルのキャッシュで十分です。

私のおすすめ:

  1. 問題は、COUNT(r) が BigInteger を Java に返し、これをオブジェクトにキャストしてキャッシュできないことだと思います。クエリで addScalar("count", Hibernate.LONG) を呼び出して、Hibernate に別のタイプの LONG を使用するように指示できます。blog.pfa-labs.com/2009/12/caching-raw-sql-count-with-hibernate.html と、 Hibernate の Second-Level Cache を COUNT() 操作に使用できますか?を参照してください。

  2. クエリ キャッシュはこれを処理できる必要があります。第 2 レベルのキャッシュは、エンティティ オブジェクトにのみ必要です。

  3. キャッシュしようとしているオブジェクトの読み取り/書き込み動作を十分に理解してください。また、読み取り数が書き込み数よりもはるかに多いことを確認してください。そうしないと、キャッシングによるメリットがまったくないか、処理が遅くなり、データの不整合が発生する可能性があります。

  4. 一部の JDBC ドライバーはデータもキャッシュすることに注意してください。あなたのものであれば、JPA の結果に影響し、JPA はそれを認識しません。

Mike Keith の「Pro JPA 2」より: ほとんどの [JDBC] ドライバーは接続とステートメントをキャッシュします。一部のキャッシュは、基本的に JPA プロバイダーに対して透過的なテーブルまたは列の状態を追跡しますが、呼び出しのたびにデータベースにアクセスしてデータを取得する必要がないという点で、いくらか節約できます。これは通常、データが読み取り専用であるか、ドライバーがデータベース アクセスを排他的に制御することがわかっている場合にのみ、ドライバーで実行できます。

JDBC キャッシングが利用可能な場合は、ドライバー固有の構成設定を介して制御できる必要があります。

編集:

最初のクエリでは、「r.validityStart による順序」は何もしません。これを削除すると、すべてが機能します。

于 2013-03-11T07:07:20.677 に答える
1

クエリキャッシュは、クエリとパラメータの組み合わせがキーと値を識別子として構成する結果を保持します。

ドキュメントから:

  • データ キャッシュにクエリ結果の ID のキャッシュ データがない場合、キャッシュは使用されません。

  • カスタム フィールド タイプまたは BigDecimal または BigInteger フィールドのプロジェクションをもたらすクエリはキャッシュされません。

  • クエリ キャッシュは、結果セット内の実際のエンティティの状態をキャッシュしないことに注意してください。識別子の値と値の型の結果のみをキャッシュします。クエリ キャッシュは、常に第 2 レベルのキャッシュと組み合わせて使用​​する必要があります。

クエリのフィールドではなく、オブジェクト全体をフェッチすることをお勧めします。

おそらく、クエリの選択部分を無視して結果をキャッシュしています。後の部分は両方のクエリで同じであるため、同じ結果が得られます。クエリの実行順序を変更してみて、結果を観察できます。

于 2013-02-21T08:58:26.700 に答える
0

あなたの問題は二次キャッシュとは関係がないと思います - それは別のものです。キャッシュ自体は、期待される結果を変更できません。

さらに確実にするために、次のコードを試して、2 番目のクエリを開始する前に 2 番目のレベルのキャッシュをクリアすることもできます。

session.setCacheMode(CacheMode.IGNORE); // session here is the SessionFactory

それでも問題が解決しない場合は、第 2 レベル キャッシュが原因ではないことは明らかです。

于 2013-03-11T07:20:48.537 に答える