休止状態のページ付け

休止状態のページネーション

1. 概要

この記事は簡単なintroduction to Pagination in Hibernateです。 標準のHQLとScrollableResults APIを確認し、最後にHibernateCriteriaを使用したページ付けについて説明します。

参考文献:

SpringでHibernate 5をブートストラップする

Hibernate 5とSpringを統合するための迅速かつ実用的なガイド。

Hibernate継承マッピング

JPA / Hibernateを使用したさまざまな継承マッピング戦略を理解するための実用的なガイド。

Spring BootからのHibernate / JPA SQLステートメントの表示

Spring Bootアプリケーションで生成されたSQLステートメントのロギングを構成する方法を学びます。

2. HQLおよびsetFirstResultsetMaxResultsAPIによるページ付け

Hibernateでページネーションを行う最も簡単で一般的な方法はusing HQLです。

Session session = sessionFactory.openSession();
Query query = sess.createQuery("From Foo");
query.setFirstResult(0);
query.setMaxResults(10);
List fooList = fooList = query.list();

この例では、基本的なFooエンティティーを使用しており、JQL実装のJPAと非常によく似ています。唯一の違いは、クエリ言語です。

logging for Hibernateをオンにすると、次のSQLが実行されていることがわかります。

Hibernate:
    select
        foo0_.id as id1_1_,
        foo0_.name as name2_1_
    from
        Foo foo0_ limit ?

2.1. 総数と最後のページ

ページネーションソリューションは、the total number of entitiesを知らなければ完全ではありません。

String countQ = "Select count (f.id) from Foo f";
Query countQuery = session.createQuery(countQ);
Long countResults = (Long) countQuery.uniqueResult();

そして最後に、総数と指定されたページサイズから、the last pageを計算できます。

int pageSize = 10;
int lastPageNumber = (int) (Math.ceil(countResults / pageSize));

この時点で、a complete example for paginationを確認できます。ここで、最後のページを計算して取得しています。

@Test
public void givenEntitiesExist_whenRetrievingLastPage_thenCorrectSize() {
    int pageSize = 10;
    String countQ = "Select count (f.id) from Foo f";
    Query countQuery = session.createQuery(countQ);
    Long countResults = (Long) countQuery.uniqueResult();
    int lastPageNumber = (int) (Math.ceil(countResults / pageSize));

    Query selectQuery = session.createQuery("From Foo");
    selectQuery.setFirstResult((lastPageNumber - 1) * pageSize);
    selectQuery.setMaxResults(pageSize);
    List lastPage = selectQuery.list();

    assertThat(lastPage, hasSize(lessThan(pageSize + 1)));
}

3. HQLとScrollableResults APIを使用したHibernateによるページ付け

ScrollableResultsを使用してページネーションを実装すると、reduce database callsになる可能性があります。 このアプローチでは、プログラムがスクロールするときに結果セットがストリーミングされるため、クエリを繰り返して各ページを埋める必要がなくなります。

String hql = "FROM Foo f order by f.name";
Query query = session.createQuery(hql);
int pageSize = 10;

ScrollableResults resultScroll = query.scroll(ScrollMode.FORWARD_ONLY);
resultScroll.first();
resultScroll.scroll(0);
List fooPage = Lists.newArrayList();
int i = 0;
while (pageSize > i++) {
    fooPage.add((Foo) resultScroll.get(0));
    if (!resultScroll.next())
        break;
}

このメソッドは、時間効率が良い(1回のデータベース呼び出しのみ)だけでなく、ユーザーがtotal count of the result set without an additional queryにアクセスできるようにします。

resultScroll.last();
int totalResults = resultScroll.getRowNumber() + 1;

一方、スクロールは非常に効率的ですが、大きなウィンドウはかなりの量のmemoryを占める可能性があることに注意してください。

4. Criteria APIを使用したHibernateによるページ付け

最後に、基準を使用してa more flexible solutionを見てみましょう。

Criteria criteria = session.createCriteria(Foo.class);
criteria.setFirstResult(0);
criteria.setMaxResults(pageSize);
List firstPage = criteria.list();

Hibernate CriteriaクエリAPIを使用すると、Projectionオブジェクトを使用してget the total countも非常に簡単になります。

Criteria criteriaCount = session.createCriteria(Foo.class);
criteriaCount.setProjection(Projections.rowCount());
Long count = (Long) criteriaCount.uniqueResult();

ご覧のとおり、このAPIを使用すると、プレーンなHQLよりも冗長なコードが最小限になりますが、the API is fully type safe and a lot more flexibleになります。

5. 結論

この記事は、Hibernateでページネーションを行うさまざまな方法の簡単な紹介です。

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