はじめに
今回は、応用編第2回としてConditionBean/OutsideSql(外だしSQL)両方における「ページング検索」を見て行きます。
ページング検索
ページング検索とは?
まずは基本的な概念から説明します。
ここで言うページング検索とは、以下のようなページングナビゲーションを
付与する検索画面の検索処理を前提としてます。
そして、これら要素満たすために必要な処理は以下の3つの処理です。
ポイントは、「ページングなし件数取得」です。
これをやらないと総ページ数などの計算ができません。
この処理のSQLは、「ページング実データ検索」と同じ条件で、
ページングの絞込みだけがないものになります。
仕組みによってはここでWhere句の条件が二箇所にわたる冗長管理に
なってしまったりと、開発者の頭を悩ませるきっかけになったりします。
ConditionBeanによるページング検索
では、さっそくConditionBeanにおけるページング検索をみてみましょう。
普通の検索
まずは普通の検索を見て下さい。
ページング検索
そして、ページング検索をするための条件を付与してみます。
必要なのは「1ページは何件か?(ページサイズ)」と「何ページ目を検索したいか?(検索対象ページ番号)」の情報です。
ConditionBeanによるページング検索は以下のようになります。
paging()にて「1ページは何件か?(ページサイズ)」と
「何ページ目を検索したいか?(検索対象ページ番号)」を指定しています。
そして、selectList()の代わりにselectPage()を利用しています。
戻り値はPagingResultBeanというクラスです。
実は、このselectPage()とPagingResultBeanで、先述の「ページング処理の基本」が
全て完了しています。計算結果を取得してみましょう。
「総ページ数」や「前ページがあるか否」などがPagingResultBeanから取得できます。
先ほどの「ページングナビゲーションの例」に照らし合わせて見ましょう。
ページングナビゲーションで必要な情報の計算処理はDBFluteが行います。
そのため、ここでよくありがちな「1ページ多い」とか
「次のページないのにあるように扱ってる」などの計算ミスは発生しません。
このようにDBFluteは、ページング処理をできるだけ安全に実装できるように工夫をしています。
ページング絞込みのSQL
ページングの絞込みは、SQLレベルで行います。
Oracleであれば「rownum」、PostgreSQLであれば「limit/offset」など
できる限りデータベースの機能を使って絞り込みます。
もし、サポートしていないデータベースに関しては、ResultSetの
ループ処理にて絞込みをします。
関連補足:最初のn件を取得
「最初のn件を取得する」という検索専用のメソッドも用意されています。
OutsideSql(外だしSQL)によるページング検索
それでは、OutsideSql(外だしSQL)によるページング検索です。
Auto/Manual (ConditionBeanとの違い)
ConditionBeanとは違う大きなポイントをまず説明します。
こちらの場合は、SQL自体は手動で作成するため、ページング絞りのSQLは
自分で実装することになります(rownumやlimit/offsetなど)。
しかし、そのSQLを自分で書くのは結構敷居の高い作業でもあります。
DBFluteでは以下の二通りのやり方を提供しています。
- AutoPaging : ResultSetレベルでの自動絞り込み
- ManualPaging : SQLレベルでの手動絞込み(自分で書く)
例えば、Oracleのrownumでの絞り込みは記述が大変で間違えやすいものです。
それならば、本当にパフォーマンスを厳密に意識したい場合にだけ「ManualPaging」で、
そうでない場合は「AutoPaging」でサクっと実装してしまう、などという
オプション的なやり方が考えられます。
「ResultSetレベルでの自動絞り込み」も利用可能なDBの場合は、
「空回し」ではなく「カーソルによるすっ飛ばし(ポインタずらし)」で
実現するため、大きなパフォーマンス劣化に結びつく可能性は
そこまで大きくないと思われます。
普通の検索
まずは、普通の検索を見てみます。
ページング検索 (AutoPaging)
そして、ページング検索です。ここではまず「AutoPaging」を使って説明します。
SQLは以下のようになります。
普通の検索と違うところが二点あります。
- 1.
- ParameterBeanの指定に「extends SPB」という宣言が追加されています。
- これは生成するParameterBeanが「SimplePagingBean」というクラスを継承することを示し、
ページング絞りのための情報を扱うことのできるParameterBeanが生成されます。
- 2.
- Select句、Join句、OrderBy句が「/*IF pmb.isPaging()*/」にて囲われています。
- このif文は、「ページングなし件数取得」と「ページング実データ検索」を区別しています。
- DBFluteでは、ページングのSQLを「ページングなし件数取得」と「ページング実データ検索」とで分ける必要はありません。このような形で共存させることによって、一番変わりやすいWhere句の条件を両方の処理にて再利用しています。
- 先述の「extends SPB」の宣言をすることによって、isPaging()が利用可能になります。
それでは実装を見てましょう。
やりたいことはConditionBeanと変わりません。
「1ページは何件か?(ページサイズ)」と「何ページ目を検索したいか?(検索対象ページ番号)」
を指定して、PagingResultBeanを受け取ります。
このように「ページングを扱えるParameterBean」を使って、
OutsideSqlでもselectPage()が利用できます。
ここではautoPaging()を呼び出しているので、ページング絞りは自動に行われます。
ページング検索 (ManualPaging)
おおよその流れはAutoPagingで学びました。
ManualPagingは、その流れの中で二点だけ違いがあります。
まずは、SQLに自分でページングの絞り込み条件を付与する必要があります。
「ページング実データ検索」の処理のときにページング絞りの条件が
有効になるように記述します。limit/offsetのサイズはParameterBeanで
計算されたものを利用します。Manualとは言っても、絞り込み条件の
計算処理はDBFluteが行うため、そこは安全に実装できます。
記述方法はDBによって異なります。
この場合は、H2データベースを利用しており、バインド変数ではなく
「埋め込み変数コメント」を利用しています。
例えば、Oracleだと以下のようになります。
そして、実装の方では、autoPaging()メソッドを呼び出しているところが、
manualPaging()に変わります。
まとめ
ページング検索をみてみました。
こちらも実務で非常に役に立つ機能です。ぜひ利用してみて下さい。
さて、今回でこの連載はおしまいです。
最後に、DBFluteに触れてみる際にとても役に立つExampleを紹介します。
- DBFluteBasicExample
- SVN : https://www.seasar.org/svn/sandbox/dbflute/
Project : trunk/dbflute-basic-example
DBFluteの一番基本となるExampleでBehaviorやConditionBeanなどの
使い方を一つ一つ「JUnitの単体テスト」の形式で実装して紹介しています。
組み込みデータベース「H2」を利用しているため、SVNからチェックアウト
するだけで、それらテストケースを実行して試すことが可能です。
環境的な面・実装的な面両方で参考になるExampleです。
- DBFluteNBasicExample
- SVN : https://www.seasar.org/svn/sandbox/dbflute/
Project : trunk/dbflute-nbasic-example
実はDBFluteはC#版も用意されています。
そのC#版DBFluteを使ったExampleです。
C#ユーザの方はぜひご覧になられて下さい。
- DBFluteSpringExample
- SVN:https://www.seasar.org/svn/sandbox/dbflute/
Project : trunk/dbflute-spring-example
実はDBFluteはSpring Frameworkでも動作します。
SpringでDBアクセス周りに困っている方はぜひご覧になられて下さい。
それでは、おしまいです。
今までありがとうございました。