This is a cache of http://dbflute.seasar.org/ja/manual/topic/programming/dbaccess/cursorselect.html. It is a snapshot of the page at 2024-11-13T00:41:20.221+0000.
DBアクセス - カーソル検索 | <strong>dbflute</strong>

そもそもカーソル検索とは?

カーソル検索とは?

多くの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もあります。