同一の SELECT ステートメントに対して同時に開かれている複数の ResultSet オブジェクト

同一の SELECT ステートメントに対して複数の ResultSet オブジェクトを開いたままにしておくと、JDBC アプリケーションが SQL を静的に実行するときに、特定の条件下では問題が起きる可能性があります。
これらの条件とは、以下のとおりです。
  • 複数の ResultSet オブジェクトが単一の接続によって開かれた。
  • 複数の ResultSet オブジェクトが、同一のカーソル属性を持つ同一の SELECT ステートメントによって戻された。
  • 同時に複数の ResultSet オブジェクトが開かれたままになっている。
  • 複数の ResultSet オブジェクトが、WHERE CURRENT OF 節を含む UPDATE または DELETE ステートメントによって変更される。

例えば、以下のコードを考慮してください。

ResultSet rs1, rs2;

Statement stmt1 = jdbcCon.createStatement(TYPE_FORWARD_ONLY, CONCUR_UPDATABLE);
rs1 = stmt1.executeQuery("SELECT * FROM SCH1.TBL1");
rs1.next();

Statement stmt2 = jdbcCon.createStatement(TYPE_FORWARD_ONLY, CONCUR_UPDATABLE);
rs2 = stmt2.executeQuery("SELECT * FROM SCH1.TBL1");
rs2.next();

PreparedStatement pStmt = jdbcCon.prepareStatement(
	"DELETE FROM SCH1.TBL1 WHERE CURRENT OF " +
	rs2.getCursorName());

SQL ステートメントが動的に実行された場合、このコードにはあいまいさはありません。 ただし、それらが静的に実行され、複数の ResultSet オブジェクトが同じ接続で開かれていた場合、問題が起きます。 pureQuery が JDBC アプリケーションの SQL ステートメントを取り込む場合、同一ステートメントは一度だけ取り込みます。 この例では pureQuery が SQL ステートメントを取り込んだ後に、pureQueryXML ファイルには SELECT * FROM SCH1.TBL1 の 1 インスタンスが含まれます。pureQuery が名前 DB_PDQ_SPC7 をカーソルに割り当てる場合、pureQueryXML ファイルにも 1 インスタンスの DELETE FROM SCH1.TBL1 WHERE CURRENT OF DB_PDQ_SPC7 が含まれます。

実行時に、WHERE CURRENT OF 節内でカーソル名だけを使用している場合、pureQuery は 2 つの ResultSet オブジェクトのどちらに DELETE ステートメントのカーソルが作用するかを判別できません。

これよりも不明瞭な状況でも、同じあいまいさが起きる可能性があります。 例えば、SELECT ステートメントが ResultSet オブジェクトを戻すメソッド内にあり、UPDATE または DELETE WHERE CURRENT OF ステートメントが ResultSet オブジェクトを入力パラメーターとして受け取るメソッド内にあるという場合があります。

問題がアプリケーションのソース・コード内にあるため明瞭でない場合もあります。 この理由から、pureQuery は、複数の ResultSet オブジェクトが現在単一の SELECT ステートメントに対して開かれていることを検出した場合の警告をログに記録します。

問題を回避する技法

このあいまいさを解決するために試行できる技法があります。

WHERE CURRENT OF 節を使用しない
代わりに、updateRow()、deleteRow()、および insertRow() メソッドを使用します。 この方法は、WHERE CURRENT OF を使用するステートメントの指定よりもオブジェクト指向が高くなります。
Statement stmt1 = jdbcCon.createStatement(TYPE_FORWARD_ONLY,CONCUR_UPDATABLE);
rs1 = stmt.executeQuery("SELECT * FROM SCH1.TBL1");
rs1.next();

Statement stmt2 = jdbcCon.createStatement(TYPE_FORWARD_ONLY,CONCUR_UPDATABLE);
rs2 = stmt.executeQuery("SELECT * FROM SCH1.TBL1");
rs2.next();

rs2.deleteRow(); 
WHERE CURRENT OF 節を UPDATE または DELETE ステートメントで使用する必要がある場合は、以下の例で示すように、ステートメントを準備または実行するときに ResultSet.getCursorName() への呼び出しを組み込むことができます。
PreparedStatement pStmt = jdbcCon.prepareStatement(
	"DELETE FROM SCH1.TBL1 WHERE CURRENT OF " +
	rs2.getCursorName());
実行時に、pureQuery は getCursorName() メソッドを UPDATE または DELETE ステートメントと関連付け、それにより正しい ResultSet オブジェクトをステートメントと関連付けることができます。 この関連付けは、getCursorName() メソッドが以下の 2 つの場所のいずれかにある場合にのみ保証されます。
  • PreparedStatement を作成するメソッド呼び出し内に、SQL ステートメントと一緒にある。
  • ステートメント・オブジェクトの execute() または executeUpdate() メソッド内。
アプリケーションが ResultSet オブジェクトを、WHERE CURRENT OF 節を含む UPDATE または DELETE ステートメントで操作する場合は、同一の SELECT ステートメントに対して同時に開かれている複数の ResultSet オブジェクトを操作するために 1 つの接続を使用するということは決してしないでください。
このトピックで最初に示されているコードは、ResultSet オブジェクトが別個の接続によって開かれている場合に機能します。

DB2 for z/OS 上での同一の SQL SELECT ステートメントの実行を可能にするプロパティー

同一の静的 SQL SELECT ステートメントを DB2 for z/OS に対して実行し、複数の同一の ResultSet オブジェクトを開く必要がある場合、まず IBM Data Server Driver for JDBC and SQLJ のプロパティー db2.jcc.allowSqljDuplicateStaticQueriesYES または TRUE に設定する必要があります。

関連概念
VALUES ステートメント
XQuery ステートメント
関連資料
DB2 パッケージに対する EXECUTE の付与
静的 SQL ステートメントが参照するデータベース・オブジェクトに影響する SQL DDL ステートメント
特殊レジスターを設定する SQL ステートメント

フィードバック