実験している問題は、HINT_PASS_DISTINCT_THROUGH
の使用方法に関係しています。 ヒント。
このヒントを使用すると、DISTINCT
がHibernateであることを示すことができます。 SELECT
ではキーワードを使用しないでください データベースに対して発行されたステートメント。
この事実を利用して、DISTINCT
に含まれていないフィールドでクエリを並べ替えることができます。 列リスト。
しかし、それはこのヒントの使用方法ではありません。
このヒントは、DISTINCT
を適用するかどうかに違いがないことが確実な場合にのみ使用する必要があります。 SQLのキーワードSELECT
SELECT
であるため、ステートメント ステートメントはすでにすべての個別の値をフェッチしますそれ自体 。アイデアは、不要なDISTINCT
の使用を回避してクエリのパフォーマンスを向上させることです。 ステートメント。
これは通常、query.distinct
を使用したときに発生することです。 条件クエリのメソッドであり、join fetching
している 子供の関係。 このすばらしい記事
@VladMihalceaのは、ヒントがどのように機能するかを詳細に説明しています。
一方、ページングを使用すると、OFFSET
が設定されます。 およびLIMIT
-または基盤となるデータベースに応じて同様のもの-SQLSELECT
データベースに対して発行されたステートメント。クエリの結果の最大数に制限されます。
前述のように、HINT_PASS_DISTINCT_THROUGH
を使用する場合 ヒント、SELECT
ステートメントにはDISTINCT
は含まれません キーワードと、あなたの結合のために、それはあなたの主要な実体の重複した記録を与える可能性があります。 query.distinct
を使用しているため、このレコードは重複を区別するためにHibernateによって処理されます。 、そして実際には必要に応じて重複を削除します。これが、Pageable
で要求されたよりも少ないレコードを取得する理由だと思います 。
DISTINCT
のように、ヒントを削除した場合 キーワードは、データベースに送信されるSQLステートメントで渡されます。メインエンティティの情報のみをプロジェクトする限り、LIMIT
で示されるすべてのレコードをフェッチします。 これが、常に要求された数のレコードを提供する理由です。
fetch join
を試すことができます 子エンティティ(join
だけでなく) 彼らと)。これにより、DISTINCT
の列で並べ替える必要のあるフィールドを使用できないという問題が解消されます。 キーワードに加えて、今では合法的にヒントを適用できるようになります。
ただし、そうすると別の問題が発生します。結合フェッチとページ付けを使用してメインエンティティとそのコレクションを返す場合、Hibernateはデータベースレベルでページ付けを適用しなくなります。OFFSET
は含まれません。 またはLIMIT
SQLステートメントのキーワード。結果をメモリにページ付けしようとします。これは有名なHibernateHHH000104
です 警告:
HHH000104: firstResult/maxResults specified with collection fetch; applying in memory!
@VladMihalceaは、これ の最後の部分で詳細に説明しています。 記事。
彼はまた、あなたの問題に対する1つの可能な解決策、ウィンドウ関数を提案しました。 。
ユースケースでは、Specification
を使用する代わりに s、アイデアはあなたがあなた自身のDAOを実装するということです。このDAOは、EntityManager
にアクセスする必要があるだけです。 、@PersistenceContext
を挿入できるため、それほど大きな問題ではありません。 :
@PersistenceContext
protected EntityManager em;
このEntityManager
を入手したら 、提供されたPageable
に基づいて、ネイティブクエリを作成し、ウィンドウ関数を使用してビルドできます。 情報、データベースに対して発行される正しいSQLステートメント。これにより、並べ替えに使用するフィールドや必要なものについて、より多くの自由が得られます。
最後に引用した記事が示すように、ウィンドウ関数はすべての市長データベースでサポートされている機能です。
PostgreSQLの場合、公式ドキュメント<で簡単に見つけることができます。 / a> 。
最後に、@ nickshoeによって実際に提案され、記事 彼は、並べ替えとページングのプロセスを2つのフェーズで実行することです。最初のフェーズでは、子エンティティを参照し、ページングと並べ替えを適用するクエリを作成する必要があります。このクエリを使用すると、プロセスの第2フェーズで、メインエンティティ自体を取得するために使用されるメインエンティティのIDを特定できます。
前述のカスタムDAOを利用して、このプロセスを実行できます。