(外だしSQL)selectCursor()

"外だしSQLの使い方" を既に読んでいることが前提となります。

概要

外だしSQLでカーソル検索 をします。DBFluteにおけるカーソル検索とはどのようなものか?について説明するページがあります。

会話上では、(外だしSQLの)せれくとかーそる と表現します。

実装方法

TypeSafeCursorを用意

外だしSQLでは、カーソルを TypeSafeCursor (ResultSetのタイプセーフなラッパークラス)で取扱います。このクラスは、Sql2Entity タスクで自動生成します。SQLファイルの中で、CustomizeEntityマークと共に TypeSafeCursorマーク を定義して、Sql2Entityを実行するようにして下さい。 (EMechaで作成するときは、"Use Cursor" にチェックを付ければOK)

e.g. SimpleMemberという名前のTypeSafeCursorを生成 @SQL-File
-- #df:entity#
-- +cursor+
select ...
  from ...

メソッドの呼び出し ※1.1.x (Java8版)

outsideSql() の後、selectCursor() を呼び出します。

e.g. 外だしSQLのカーソル検索の実装手順 (Eclipseでコード補完) @Java
memberBhv.o // .o と打って enter
--
memberBhv.outsideSql().selCu // .selCu と打って enter
--
memberBhv.outsideSql().selectCursor(pmb, handler);

メソッドはオーバーロードで "定型呼び出し形式" と "フリースタイル形式" と二つあります。

メソッドの呼び出し ※1.0.x (Java6版)

outsideSql() の後、cursorHandling() を呼び出し、その後 selectCursor() を呼び出します。

e.g. 外だしSQLのカーソル検索の実装手順 (Eclipseでコード補完) @Java
memberBhv.o // .o と打って enter
--
memberBhv.outsideSql().cu // .cu と打って enter
--
memberBhv.outsideSql().cursorHandling().s // .s と打って enter
--
memberBhv.outsideSql().cursorHandling().selectCursor(pmb, handler);

定型呼び出し形式

TypedParameterBean を引数に受け取ります。(null は許可されません)

ただし、カーソル検索は戻り値Entityを利用しない検索であり、CursorHandler は別途引数で指定します。

TypeSafeCursorマーク付きのCustomizeEntityマーク、および、ParameterBeanマークが定義されていることで TypedParameterBean の定型呼び出しが利用できます。

フリースタイル形式

パス、パラメータ(ParameterBean)、CursorHandlerの型の三つの要素を引数に受け取ります。 パスとパラメータに関しては、それぞれフリースタイル形式の基本仕様と全く同じです。

外だしSQLの使い方 - フリースタイル基本仕様

CursorHandlerの実装

第二引数、もしくは、第三引数の handler に、Sql2Entityで自動生成された CursorHandlerの実装クラス(TypeSafeCursor)の fetchCursor() メソッド をオーバーライドしたクラスのインスタンスを指定します。fetchCursor() では、Cursor クラスを参照して業務処理を実装します。 selectCursor() の処理の中で、この fetchCursor() がコールバックされます。

e.g. TypeSafeCursorの利用 @Java
memberBhv.outsideSql()
.selectCursor(PATH_sel..., pmb, new PurchaseSummaryMemberCursorHandler() {
    public Object fetchCursor(PurchaseSummaryMemberCursor cursor) thr... {
        while (cursor.next()) { // on memory per one record
            // type safe access
            Integer memberId = cursor.getMemberId();
            String memberName = cursor.getMemberName();
            Date birthdate = cursor.getBirthdate();
            Timestamp formalizedDatetime = cursor.getFormalizedDatetime();
            Long purchaseSummary = cursor.getPurchaseSummary();

            ...
        }
        return null; // basically null
    }
});

カーソルのループのやり方

fetchCursor() の中で、引数の cursor をループさせてデータを取得します。next() メソッドで次の行があるかどうかを判定し、最後の行までループさせます。

データの(タイプセーフな)取得

それぞれのカラムのデータの取得は、Entity の時と同じように、get[名前] で取得することができます。内部的には、このメソッドの中で ResultSet のメソッドを呼び出してデータを取得しています。

selectCursor()の戻り値

fetchCursor() の戻り値が、そのまま selectCursor() の戻り値となります。この CursorHandler の中で業務的な処理が終了するのであれば、基本的に null 固定で問題ありません。

スカラ値では受け取れない

外だしSQLのカーソル検索では、必ず TypeSafeCursor を利用しますので、select 句のカラムが一つだけでも TypeSafeCursor を自動生成して、それを利用してデータを受け取ります。

ページングのSQLでカーソル検索

ManualPagingだと工夫が必要

ページング検索のSQLをカーソル検索でも使いたい、というような場合、以下のような対応になります。

AutoPaging
気にせず利用できる
ManualPaging
少し工夫が必要

ParameterBean自体は、ページングとカーソルの両方を定義した上で自動生成することができますが、ManualPaging だと実行時にページング条件がそのまま残ってしまいます。(DBFluteは、ManualPagingのページング条件だけを自動で抑制することはできないので)

Exクラスにてboolean制御

ManualPagingの外だしSQLのページング条件部分にて、カーソル検索時はページング条件が無効になるように isCursorHandling() の条件分岐を入れます。 (代理判定メソッドの自動判別の機能を使っています)

e.g. ページングのSQLの中に、カーソル検索のための条件分岐を指定 @SQL-File
 /*IF pmb.isPaging()*/
 order by PURCHASE_MAX_PRICE desc nulls last, mb.MEMBER_ID asc
 /*IF !pmb.isCursorHandling()*/
 limit /*pmb.fetchSize*/20 offset /*pmb.pageStartIndex*/80
 /*END*/
 /*END*/

ParameterBean のExクラスにて、共有のためのboolean制御を行います。 (少し特殊ですので、別の人が見たときに迷わせないようにコメントを書いておきましょう。このドキュメントへのURLを記載しておくと良いです)

e.g. ParameterBean のExクラスにて、ListHandlingPmbとboolean制御 @Java
...
 * @author DBFlute(AutoGenerator)
 * @author jflute
 */
public class PagingWithCursorMemberPmb extends BsPagingWithListMemberPmb {

    private boolean cursorHandling;

    @Override
    public boolean isPaging() { // not to depend on framework default value
        return super.isPaging() || cursorHandling; // always true if cursor handling
    }

    @Override
    public boolean isCursorHandling() { // to suppress paging condition
        return cursorHandling;
    }

    /**
     * Use parameter-bean for cursor handling.
     */
    public void forCursorHandling() {
        cursorHandling = true;
    }
}
  • isPaging()のオーバーライドは必須ではないが、フレームワークのデフォルト値に依存しないように
  • クラスのJavaDocにauthorを付けるポリシーの場合は、ここでも忘れずに

そして、カーソル検索を実行するときに(だけ)、forCursorHandling() メソッドを呼びます。

e.g. カーソル検索であることを示して外だしSQLを実行 @Java
PagingWithCursorMemberPmb pmb = new PagingWithCursorMemberPmb();
pmb.forCursorHandling();

memberBhv.outsideSql().selectCursor(pmb, ...);

...

手動による制御なので、ページング検索側のプログラムも含めて、しっかりテストをしましょう。