外だしSQLのカーソル検索

検索結果をカーソル(ResultSet/DataReader)で扱うことのできるメソッドが存在します。

大量件数を扱う場合は、検索結果を一気にメモリ上に展開するとメモリ不足になることがあります。
カーソル(ResultSet/DataReader)経由で1件ずつ処理をすることでその問題をそれを解決します。
一番わかりやすい例としては、「検索結果をそのままCSVファイルに出力する」というのが考えられるでしょう。

実装方法

outsideSql()メソッドの後、「cursorHandling()」メソッドを呼び出します。
すると、その後に「selectCursor()」メソッドが呼び出せます。

「selectCursor()」メソッドの引数に指定するCursorHandlerでカーソル(ResultSet/DataReader)を扱うことが可能です。
「SQLのパス」や「ParameterBean」の仕様に関してはリスト検索と変わるところはありません。


final CursorHandler handler = ...;
memberBhv.cursorHandling().selectCursor(path, pmb, handler);
		

CursorHandlerは、Sql2Entityでオプション指定することにより生成することができます。

ex) Sql2Entityでカーソル指定
		
-- #PurchaseSummaryMember#
-- +cursor+

-- !PurchaseSummaryMemberPmb!
-- !!String memberStatusCode!!

select member.MEMBER_ID
     , member.MEMBER_NAME
     , (select sum(purchase.PURCHASE_COUNT)
          from PURCHASE purchase
         where purchase.MEMBER_ID = member.MEMBER_ID
       ) as PURCHASE_SUMMARY
  from MEMBER member
 where member.MEMBER_STATUS_CODE = /*pmb.memberStatusCode*/'FML'
		
		

Sql2Entityの後、生成されたCursorHandlerのfetchCursor()メソッドをオーバーライドし、カーソルを扱います。
引数のCursorのnext()メソッドでループして1件ごとのデータを取得することが可能です。

ex) 検索結果をCSVファイルへ出力する(ふり)
final PurchaseSummaryMemberCursorHandler handler = new PurchaseSummaryMemberCursorHandler() {
    public Object fetchCursor(PurchaseSummaryMemberCursor cursor) throws SQLException {
        while (cursor.next()) {
            final Integer memberId = cursor.getMemberId();
            final String memberName = cursor.getMemberName();
            final Integer purchaseSummary = cursor.getPurchaseSummary();

            // ここではただログに出力するだけ
            // (本当はCSVファイルへの出力)
            _log.debug("  " + memberId + " - " + memberName + " - " + purchaseSummary);

        }// ResultSetのCloseはFrameworkが行うので必要なし
        return null;// ここで処理が完結してるので戻り値は不要
    }
};
memberBhv.outsideSql().cursorHandling().selectCursor(path, pmb, handler);
		
それぞれのカラムの値の取得は、Sql2Entityで生成したクラスを利用していることによりタイプセーフです。
文字列の入力ミスによるSQL上の定義との食い違いでBUGが発生することはありません。
このCursorを「型付Cursor」と呼びます。型付でないCursorで扱うことも可能ですがお奨めしません。

C#版での例題の実装は以下の通りです。
Javaとの違いは無名インナークラス(C#にはない)ではなく、匿名メソッド(Javaにはない)を使っているところです。

ex) 検索結果をCSVファイルへ出力する(ふり) {C#}
PurchaseSummaryMemberCursorHandler handler = new PurchaseSummaryMemberCursorHandler(
    delegate(PurchaseSummaryMemberCursor cursor) {
        while (cursor.Next()) {
            int? memberId = cursor.MemberId;
            String memberName = cursor.MemberName;
            decimal? purchaseSummary = cursor.PurchaseSummary;
            
            _log.Debug("  " + memberId + " - " + memberName + " - " + purchaseSummary);

        }// DataReaderのCloseはFrameworkが行うので必要なし
        return null;// ここで処理が完結してるので戻り値は不要
    });
memberBhv.OutsideSql().CursorHandling().SelectCursor(path, pmb, handler);