はじめに
前回は、外だしSQL(OutsideSql)の基本を見ていきました。今回は、様々な状況に合わせた機能をみていきましょう。
一件検索
前回は、リスト検索のみを対象としていました。実際の業務は、外だしSQLを利用する場合のほとんどがリスト検索になることが(経験上)多いため、ベースとなる検索をリスト検索と位置づけて説明しました。しかし、当然一件検索もしたいことがあります。
もう一度、前回利用したSQLのサンプルを思い出しましょう。
そして、リスト検索の場合の呼び出しです。
BehaviorのoutsideSql()からselecList()を呼んでいますね。一件検索をしたいときは、outsideSql()に続いてentityHandling()を呼び出し、selectEntity()を実行します。
このselectEntity()は、検索結果が0件の時はnullを戻します。ここはConditionBeanのときの一件検索と同じで、nullだったら例外になってほしい場合は、selectEntityWithDeletedCheck()を使います。
カーソル検索
検索結果をカーソル(ResultSet)で扱うことのできるメソッドが存在します。
大量件数を扱う場合は、検索結果を一気にメモリ上に展開するとメモリ不足になることがあります。カーソル(ResultSet/DataReader)経由で1件ずつ処理をすることでその問題をそれを解決します。一番わかりやすい例としては、「検索結果をそのままCSVファイルに出力する」というのが考えられるでしょう。
ResultSetと聞いてちょっといやな思いをする方もいらっしゃるでしょう。JDBCバリバリで実装してたころにResultSetの使いづらいインターフェースに悩まされた記憶がよみがえりませんでしょうか? そして、「結局O/Rマッパを使っているのにResultSet直触りするのかぁ」と思いそうなのですが、自動生成を武器にしているDBFluteはそんなことはしません。
外だしSQLのSql2Entityの宣言のところにオプションを示す一行を追加します。「--」に続けて「+cursor+」と記述します。
これでSql2Entityを実行します。
すると、前回生成したような戻り値Entityではなく、「exdao.cursor」配下にCursorを扱うクラスが生成されます。実際に使ってみましょう。
コールバックのハンドラを実装してメソッドに渡しています。メソッドは、outsideSql()に続いてcursorHandling()を呼び出した後のselectCursor()です。ここではResultSetは登場していませんが、1件ずつメモリ上に展開しての処理がされています。
そして、オーバーライドしたメソッドの引数から「タイプセーフ」にカラムの値が取得できています。
DBFluteの発想としては、「Sql2EntityでSelect句の構成がわかるのだから、文字列とか番号インデックスなんかで指定するのではなく、タイプセーフにアクセスできるようにしてしまおう」というものです。実際にはResultSetをラップしているだけではありますが、直接ResultSetを扱うときに比べてはるかに「安全な実装」が可能になっています。
また、コールバック形式の処理なので、カーソル(ResultSet)のClose処理を気にする必要はありません。このコールバックを呼び出すフレームワーク内で解決しますので、開発者はただループを回してタイプセーフに値を取って本来一番大事な業務処理の実装に集中することができます。
アプリケーションで1、2箇所はこういった大量件数で1件ずつ処理したいことが少なからずあります。このDBFluteのカーソル検索ならいざそういった場面が来ても安心して実装可能です。
まとめ
外だしSQLでの細かい、かつ、よくある場面に対する対応をみてきました。
DBFluteは、外だしSQLを重要視しています。ConditionBeanでできない複雑なSQLを実装するのは非常に大変な作業ですから、極力業務処理とは無関係な余計なところで悩みたくないものです。できる限り安全な実装ができるような支援を心がけています。
次回は、ConditionBean、外だしSQL双方にまたがる応用編「ページング」、「エスケープ付き曖昧検索」を見ていきましょう。