DBアクセスを定番化しよう DBFlute入門

第8回 応用編「ページング検索」

この記事を読むのに必要な時間:およそ 5 分

はじめに

今回は,応用編第2回としてConditionBean/OutsideSql(外だしSQL)両方における「ページング検索」を見て行きます。

ページング検索

ページング検索とは?

まずは基本的な概念から説明します。 ここで言うページング検索とは,以下のようなページングナビゲーションを 付与する検索画面の検索処理を前提としてます。

図1 ページングナビゲーションの例

図1 ページングナビゲーションの例

そして,これら要素満たすために必要な処理は以下の3つの処理です。

リスト1 ページング処理の基本

1. ページングなし件数取得
    → ページングをしなかった場合の件数を取得し,総ページ数などの導出要素とする。
    → ページング検索画面のページングナビゲーションの実装において必須の処理となる。

2. ページング実データ検索
    → ページング絞りの条件で絞られた取得したい実データのリスト検索。

3. ページング結果計算処理
    → ページングナビゲーションンで利用するページング結果の計算処理。(総ページ数の導出や次ページ有無判定など)
    → ページング検索画面のページングナビゲーションの実装において必須の処理となる。

ポイントは,「ページングなし件数取得」です。 これをやらないと総ページ数などの計算ができません。 この処理のSQLは,「ページング実データ検索」と同じ条件で, ページングの絞込みだけがないものになります。

仕組みによってはここでWhere句の条件が二箇所にわたる冗長管理に なってしまったりと,開発者の頭を悩ませるきっかけになったりします。

ConditionBeanによるページング検索

では,さっそくConditionBeanにおけるページング検索をみてみましょう。

普通の検索

まずは普通の検索を見て下さい。

リスト2 ConditionBeanによる普通の検索

// 取得:会員スタータスを結合して取得
// 条件:会員アカウントが'S'で始まること
// 並び:会員IDの昇順
MemberCB cb = new MemberCB();
cb.setupSelect_MemberStatus();
cb.query().setMemberAccount_PrefixSearch("S");
cb.query().addOrderBy_MemberId_Asc();

List<Member> memberList = memberBhv.selectList(cb);

for (Member member : memberList) {
    ...
}
ページング検索

そして,ページング検索をするための条件を付与してみます。 必要なのは「1ページは何件か?(ページサイズ)」「何ページ目を検索したいか?(検索対象ページ番号)」の情報です。

ConditionBeanによるページング検索は以下のようになります。

リスト3 ConditionBeanによるページング検索

// 取得:会員スタータスを結合して取得
// 条件:会員アカウントが'S'で始まること
// 並び:会員IDの昇順
MemberCB cb = new MemberCB();
cb.setupSelect_MemberStatus();
cb.query().setMemberAccount_PrefixSearch("S");
cb.query().addOrderBy_MemberId_Asc();

// 1ページ20件で3ページ目を検索
cb.paging(20, 3);// 

// ページング検索
//   1. ページングなし件数取得
//   2. ページング実データ検索
//   3. ページング結果計算処理
PagingResultBean<Member> memberPage = memberBhv.selectPage(cb); 

for (Member member : memberPage) {
    ...
}

paging()にて「1ページは何件か?(ページサイズ)」と 「何ページ目を検索したいか?(検索対象ページ番号)」を指定しています。 そして,selectList()の代わりにselectPage()を利用しています。 戻り値はPagingResultBeanというクラスです。

実は,このselectPage()とPagingResultBeanで,先述の「ページング処理の基本」が 全て完了しています。計算結果を取得してみましょう。

リスト4 ConditionBeanによるページング検索の結果

PagingResultBean<Member> memberPage = memberBhv.selectPage(cb); 

// 3ページ目(41件目から60件目)のリスト
for (Member member : memberPage) {
    ...
}

// ページングなし条件での総件数
int allRecordCount = memberPage.getAllRecordCount();

// 総ページ数
int allPageCount = memberPage.getAllPageCount();

// 対象ページ番号
int currentPageNumber = memberPage.getCurrentPageNumber();

// 前ページがあるか否か
boolean isExistPrePage = memberPage.isExistPrePage();

// 次ページがあるか否か
boolean isExistNextPage = memberPage.isExistNextPage();

// ナビゲーションのページリンク候補一覧
memberPage.setPageRangeSize(5);
List<Integer> pageNumberList = memberPage.pageRange().createPageNumberList();

// ...その他省略

「総ページ数」「前ページがあるか否」などがPagingResultBeanから取得できます。 先ほどの「ページングナビゲーションの例」に照らし合わせて見ましょう。

図2 ページングナビゲーションとページング結果

図2 ページングナビゲーションとページング結果

ページングナビゲーションで必要な情報の計算処理はDBFluteが行います。 そのため,ここでよくありがちな「1ページ多い」とか 「次のページないのにあるように扱ってる」などの計算ミスは発生しません。 このようにDBFluteは,ページング処理をできるだけ安全に実装できるように工夫をしています。

ページング絞込みのSQL

ページングの絞込みは,SQLレベルで行います。 Oracleであれば「rownum」,PostgreSQLであれば「limit/offset」など できる限りデータベースの機能を使って絞り込みます。 もし,サポートしていないデータベースに関しては,ResultSetの ループ処理にて絞込みをします。

関連補足:最初のn件を取得

「最初のn件を取得する」という検索専用のメソッドも用意されています。

リスト5 fetchFirst()を使った検索

// 取得:会員スタータスを結合して取得
// 条件:会員アカウントが'S'で始まること
// 並び:会員IDの昇順
MemberCB cb = new MemberCB();
cb.setupSelect_MemberStatus();
cb.query().setMemberAccount_PrefixSearch("S");
cb.query().addOrderBy_MemberId_Asc();

// 最初の20件
cb.fetchFirst(20); 

// 検索結果の最初の20件だけを取得
List<Member> memberList = memberBhv.selectList(cb); 

for (Member member : memberList) {
    ...
}

著者プロフィール

久保雅彦(くぼまさひこ)

DBFluteメインコミッタ。主にオープン系の開発に従事。DB設計・DB周りの実装などを担当することが多い。

コメント

コメントの記入