1. 概要

この記事では、Java PersistenceAPIページネーションを実装する方法について説明します。

基本的なJQLとよりタイプセーフなCriteriaベースのAPIを使用してページングを行う方法を説明し、各実装の利点と既知の問題について説明します。

2. JQLとsetFirstResult() setMaxResults()APIを使用したページ付け

ページネーションを実装する最も簡単な方法は、 Javaクエリ言語を使用することです–クエリを作成し、 setMaxResults およびsetFirstResultを介して構成します。

Query query = entityManager.createQuery("From Foo");
int pageNumber = 1;
int pageSize = 10;
query.setFirstResult((pageNumber-1) * pageSize); 
query.setMaxResults(pageSize);
List <Foo> fooList = query.getResultList();

APIは単純です:

  • setFirstResult(int):ページネーションを開始する結果セットのオフセット位置を設定します
  • setMaxResults(int):ページに含める必要のあるエンティティの最大数を設定します

2.1. 総数と最後のページ

より完全なページネーションソリューションについては、合計結果数も取得する必要があります。

Query queryTotal = entityManager.createQuery
    ("Select count(f.id) from Foo f");
long countResult = (long)queryTotal.getSingleResult();

最後のページの計算も非常に便利です。

int pageSize = 10;
int pageNumber = (int) ((countResult / pageSize) + 1);

結果セットの総数を取得するこのアプローチでは、(カウントのための)追加のクエリが必要であることに注意してください。

3. エンティティのIDを使用したJQLによるページ付け

簡単な代替のページネーション戦略は、最初に完全なIDを取得し、次にこれらに基づいて完全なエンティティを取得することです。 これにより、エンティティフェッチをより適切に制御できますが、IDを取得するにはテーブル全体をロードする必要があることも意味します。

Query queryForIds = entityManager.createQuery(
  "Select f.id from Foo f order by f.lastName");
List<Integer> fooIds = queryForIds.getResultList();
Query query = entityManager.createQuery(
  "Select f from Foo e where f.id in :ids");
query.setParameter("ids", fooIds.subList(0,10));
List<Foo> fooList = query.getResultList();

最後に、完全な結果を取得するには2つの異なるクエリが必要であることに注意してください。

4. CriteriaAPIを使用したJPAによるページネーション

次に、 JPA CriteriaAPIを活用してページ付けを実装する方法を見てみましょう。

int pageSize = 10;
CriteriaBuilder criteriaBuilder = entityManager
  .getCriteriaBuilder();
CriteriaQuery<Foo> criteriaQuery = criteriaBuilder
  .createQuery(Foo.class);
Root<Foo> from = criteriaQuery.from(Foo.class);
CriteriaQuery<Foo> select = criteriaQuery.select(from);
TypedQuery<Foo> typedQuery = entityManager.createQuery(select);
typedQuery.setFirstResult(0);
typedQuery.setMaxResults(pageSize);
List<Foo> fooList = typedQuery.getResultList();

これは、動的でフェイルセーフなクエリを作成することが目的の場合に役立ちます。 「ハードコードされた」、「文字列ベースの」JQLまたはHQLクエリとは対照的に、 JPA Criteria は、コンパイラがクエリエラーを動的にチェックするため、実行時のエラーを減らします。

JPA基準を使用して、エンティティの総数を簡単に取得できます。

CriteriaQuery<Long> countQuery = criteriaBuilder
  .createQuery(Long.class);
countQuery.select(criteriaBuilder.count(
  countQuery.from(Foo.class)));
Long count = entityManager.createQuery(countQuery)
  .getSingleResult();

最終結果は、JPACriteriaAPIを使用した完全なページネーションソリューションです。

int pageNumber = 1;
int pageSize = 10;
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();

CriteriaQuery<Long> countQuery = criteriaBuilder
  .createQuery(Long.class);
countQuery.select(criteriaBuilder
  .count(countQuery.from(Foo.class)));
Long count = entityManager.createQuery(countQuery)
  .getSingleResult();

CriteriaQuery<Foo> criteriaQuery = criteriaBuilder
  .createQuery(Foo.class);
Root<Foo> from = criteriaQuery.from(Foo.class);
CriteriaQuery<Foo> select = criteriaQuery.select(from);

TypedQuery<Foo> typedQuery = entityManager.createQuery(select);
while (pageNumber < count.intValue()) {
    typedQuery.setFirstResult(pageNumber - 1);
    typedQuery.setMaxResults(pageSize);
    System.out.println("Current page: " + typedQuery.getResultList());
    pageNumber += pageSize;
}

5. 結論

この記事では、JPAで使用可能な基本的なページネーションオプションについて説明しました。

いくつかには欠点があります–主にクエリのパフォーマンスに関連しますが、これらは通常、改善された制御と全体的な柔軟性によって相殺されます。

このSpringJPAチュートリアルの実装は、 GitHubプロジェクトにあります。これはMavenベースのプロジェクトであるため、そのままインポートして実行するのは簡単です。