これは次のような問題です。 HQL-ページネーションの行識別子
HQLを使用してページネーションを実装しようとしています。 PostgreSQLデータベースがあります。
_int elementsPerBlock = 10;
int page = 2; //offset = 2*10
String sqlQuery = "FROM Messages AS msg " +
" LEFT JOIN FETCH msg.commands AS cmd " +
"ORDER BY msg.identifier ASC" ;
Query query = session.createQuery( sqlQuery )
.setFirstResult( elementsPerBlock * ( (page-1) +1 ) )
.setMaxResults( elementsPerBlock );
_
何が起こるかというと、Hibernateはすべてのメッセージをフェッチし、それらがすべてロードされた後で必要なメッセージを返します。
したがって、Hibernateは、返される30個ではなく210000個のエンティティをフェッチします(各メッセージには正確に2つのコマンドがあります)。
オーバーヘッドを7000倍減らす方法はありますか?
編集:.setFetchSize( elementsPerBlock )
を追加してみました。それは助けにはならなかった。
編集2:生成されるSQLクエリは次のとおりです。
_select ...
from schemaName.messages messages0_
left outer join schemaName.send_commands commands1_
on messages0_.unique_key=commands1_.message_key
order by messages0_.unique_identifier ASC
_
絶対にLIMITやOFFSETはありません
JPA 2.0仕様 、セクション3.8.6クエリ実行、
コレクションに対するフェッチ結合を含むクエリにsetMaxResultsまたはsetFirstResultを適用した場合の影響は定義されていません。
これはデータベースごとに異なり、私の経験では、Hibernateは通常、データベースクエリレベルではなくメモリ内でページングを実行します。
私が通常行っていることは、別のクエリを使用して目的のオブジェクトのIDを取得し、それをフェッチ結合でクエリに渡すことです。
私はこのソリューションを使用しています:
/**
* @param limitPerPage
* @param page
* @return
*/
public List<T> searchByPage(int limitPerPage, int page, String entity) {
String sql = "SELECT t FROM " + entity + " t";
Query query = em.createQuery(sql)
.setFirstResult(calculateOffset(page, limitPerPage))
.setMaxResults(limitPerPage);
return query.getResultList();
}
/**
* @param page
* @return
*/
private int calculateOffset(int page, int limit) {
return ((limit * page) - limit);
}
ようこそ。
QueryとCriteriaインターフェースを使用することにより、ページ分割を実現できます。
クエリインターフェイスを使用したページ付け:
ページネーション用のQueryインターフェースには2つのメソッドがあります。
1。Query setFirstResult(int startPosition):このメソッドは、結果セットの行0から始まる最初の行を表す整数を取ります。
2。Query setMaxResults(int maxResult):このメソッドは、オブジェクトの固定数maxResultsを取得するようにHibernateに指示します。上記の2つの方法を組み合わせて使用すると、WebまたはSwingアプリケーションでページングコンポーネントを構築できます。
例:
Query query = session.createQuery("FROM Employee");
query.setFirstResult(5);
query.setMaxResults(10);
List<Employee> list = query.list();
for(Employee emp: list) {
System.out.println(emp);
}
基準インターフェースを使用したページ付け:
ページネーションのCriteriaインターフェースには2つのメソッドがあります。
1。基準setFirstResult(int firstResult):
取得する最初の結果を設定します。
2。リスト項目基準setMaxResults(int maxResults):
取得するオブジェクトの数に制限を設定します。
例:
Criteria criteria = session.createCriteria(Employee.class);
criteria.setFirstResult(5);
criteria.setMaxResults(10);
List<Employee> list = criteria.list();
for(Employee emp: list) {
System.out.println(emp);
}
commandエンティティの一部の属性に関して結果セットをフィルタリングしないため、SQL結合を回避し、messageのコマンドの遅延フェッチを構成することもできます。結合しない場合、Hibernateはデータベースのページング機能を使用します。
ただし、N + 1 seletcs issueに注意する必要があります。つまり、遅延フェッチされるcommands属性ごとに1つの選択を行わないようにします。これを回避するには、休止状態のマッピングでbatch-sizeプロパティを設定するか、hibernate.default_batch_fetch_size休止状態設定のプロパティ。
たとえば、Hibernateセッション内で100 messageオブジェクトをフェッチし、batch-sizeを10に設定した場合、Hibernateは10 commandアソシエーションをフェッチしますgetCommands()を最初に呼び出したときの10の異なるメッセージオブジェクトのmessageオブジェクト。クエリの数は、元のメッセージを取得するクエリに加えて、10に減ります。
ここを見てください: http://Java.dzone.com/articles/hibernate-tuning-queries-using?page=0,1 作者は、簡単な例のためにさまざまなフェッチ戦略を比較します
ほとんどの場合、HQLを使用して独自のクエリを作成すると、クエリビルダーメソッドはカスタムhqlクエリを解析して変更できません。したがって、LIMIT ?, ?
ステートメントをHQLクエリの最後に置いて、オフセットパラメータをバインドします。
元の例外は正しくないと思います。
何が起こるかというと、Hibernateがすべてのメッセージをフェッチし、それらがすべてロードされた後に必要なメッセージを返します。
クエリ処理中に何が起こるかは、setFirstResult(calculateOffset(page、limitPerPage))がOFFSETに変換され、setMaxResults(limitPerPage)がLIMITに変換されることです。