そもそもカーソル検索とは?
カーソル検索とは?
多くのO/Rマッパは、検索結果を全てメモリ上に保持して、その SQL(Statement) のDBとの接続は閉じます。検索結果は全て保持されているので、アプリケーションはそれ以降、DBとの関わりなしにデータを扱うことができます。 dbfluteも基本仕様はそのようになっています。
e.g. dbfluteでデータを一気にメモリ上に保持する検索(普通の検索) @Java
MemberCB cb = new MemberCB();
List<Member> memberList = memberBhv.selectList(cb);
// memberListには、検索されたデータが全て格納されている
// (この時点で、データはアプリケーション上のメモリに存在する)
一方、カーソル検索は、検索結果を全てメモリ上に保持せず、一レコードずつ業務的な処理をします。 処理し終わったレコードのデータはアプリケーション上のメモリから破棄しつつ、また次のレコードの処理を行います。 プリミティブなインターフェースである JDBC は、この方法で検索をします。(ResultSet や DataReaderなど)
e.g. JDBCでデータを一レコードずつ取得(フェッチ) @Java
ResultSet rs = ps.executeQuery();
while (rs.next()) { // 行がなくなるまでループ
// 一レコードだけメモリ上に保持
String memberName = rs.getString("MEMBER_NAME");
... // 業務的な処理
// 一ループ終わったらループ内で取得データは破棄され、
// また、次のループで次のレコードを取得する。
}
この場合、(トランザクション内での検索の)データの取得は、必ずトランザクション内で処理する必要があります。トランザクションが終わると(コミット or ロールバック)、このカーソルの接続が切断されます。
内部的にはみんなカーソル検索
実は、メモリ上に全て保持するやり方も、内部的にはカーソル検索をしています。 カーソル検索をして、全てのデータをメモリ上に保持し、全て取得し終わったらDBとの接続を切断しているのです。 基本的にO/Rマッパは、JDBC をラップして作られているためです。
メモリ上に保持する検索のメリデメ
メリット
アプリケーションの実装として利用しやすいのは、メモリ上に一気に保持する検索です。 DBとの接続の切断を気にする必要はなく、トランザクションの外でもデータを利用することができます。
デメリット
唯一、困ることは、大量データ(大量件数のレコード)を検索する場合です。 検索した大量データを全てメモリに保持することになりますので、アプリケーションのメモリが圧迫されることになります。 圧迫されるだけならまだしも、実行環境のメモリ許容量を超えてメモリのオーバーフローエラー(OutOfMemoryError)が発生して、 アプリケーションが続行できなくなる可能性があります。主には、(夜間の)バッチ処理などで、この問題を抱えることが多いです。
カーソル検索がないO/Rマッパだと
アプリケーションで利用できるメモリの量を増やしたり、データ構造を変えてそもそも扱う件数を減らしたり、 などの外部要素での回避ができれば良いですが、そうでない場合は、大半はO/Rマッパ経由で検索するけれども、 必要になったところだけでJDBCを直接利用して回避、ということになる可能性があります。 (もちろん要件次第では、アプリケーションでの大量データの扱い方の工夫次第で回避できるかもしれませんが...バッチループ方式を使うなど)
dbfluteのカーソル検索
dbfluteでは、(先述の通り)通常はメモリ上に保持する検索がメインですが、大量データを扱うようなときのためにカーソル検索を利用することもできます。
厳密には 1 ループで破棄しないことも
JDBCでデータを 1 レコードずつ取得する際ですが、ResultSetタイプの設定次第では、 次のループでも前のレコードのデータをJDBC内部で保持し続けている可能性があります。
e.g. JDBCでデータを一レコードずつ取得するけど @Java
ResultSet rs = ps.executeQuery();
while (rs.next()) { // 行がなくなるまでループ
// 1 レコードだけメモリ上に保持
String memberName = rs.getString("MEMBER_NAME");
... // 業務的な処理
// 1 ループ終わったらループ内で取得データは破棄されるのが基本だけど、
// 設定次第では、JDBC内部で持ち続けてる可能性も。
}
主なResultSetタイプは以下の通りです。
- TYPE_FORWARD_ONLY
- TYPE_SCROLL_INSENSITIVE
- TYPE_SCROLL_SENSITIVE
カーソルのスクロール(データポインタを前に戻したり)ができるタイプ(TYPE_SCROLL_XXX)にしている場合は、 一度読み込んだデータを再度(DBから)読み込み直さないように次のループになっても保持している可能性があります。 但し、これはJDBCドライバの実装次第と考えられます。
よって、大量データを扱うためのカーソル検索で、このタイプがスクロール可能なものになっていると、 なんのためにカーソル検索をしているのかわかりません(結局、メモリのオーバーフローに)。dbfluteでは、 必要になるとき(大量データを取り扱わないページング検索でカーソルのポインタシフトを使う場合など)以外の全ての検索にてデフォルトを TYPE_FORWARD_ONLY にしているので、(明示的に設定を変えない限りは)この問題が発生することはありません。 (昔々、問題が発生してこの辺を調整したことがあります...)
さらには、問答無用でメモリ上に持ってしまうDBMSもあります。