ヘッダーをスキップ
Oracle Database JDBC開発者ガイドおよびリファレンス
11gリリース1(11.1)
E05720-02
  目次
目次
索引
索引

戻る
戻る
次へ
次へ
 

17 結果セット

Java Development Kit(JDK)1.2.xの標準Java Database Connectivity(JDBC)2.0機能には、結果セット機能の拡張が含まれています。拡張には、前方または後方への処理、相対的または絶対的な位置指定、内部または外部で生じたデータベースに対する変更の参照、および結果セット・データの更新と更新による変更内容のデータベースへのコピーがあります。

この章では、これらの機能を説明します。次の項目が含まれます。

JDBC 2.0結果セットの概要

JDBC 2.0結果セットの機能とカテゴリの概要を説明します。Oracle JDBCドライバで必要な実装についても説明します。この項には、次の項目が含まれます。

JDBC 2.0でサポートされる結果セット機能および結果セット・カテゴリ

JDBC 2.0の結果セット機能には、スクロール可能性と位置指定、他からの変更に対する同期性、および更新可能性の拡張が含まれています。

  • スクロール可能性、位置指定および同期性は、結果セット型で判断されます。

  • 更新可能性は、同時実行性型で判断されます。

結果セットを作成する文オブジェクトを作成するときに、必要な結果セット型および同時実行性型を指定します。

様々な結果セット型と同時実行性型を組み合せて、6種類の異なるカテゴリの結果セットを作成できます。

スクロール可能性、位置指定および同期性

スクロール可能性とは、結果セットを前方のみでなく後方にも移動できる機能を指します。スクロール可能性には、相対的な位置指定または絶対的な位置指定によって、結果セットの特定の位置に移動できる機能が関連します。

相対的な位置指定により、現在行から前方または後方へ、指定された行数分、移動できます。絶対的な位置指定により、結果セットの先頭または末尾から数えて、指定された行番号に移動できます。

JDBC 2.0では、スクロール可能で位置指定可能な結果セットも使用可能です。

スクロール可能で位置指定可能な結果セットを作成する場合、同期性も指定する必要があります。同期性とは、結果セットの外部から基礎となるデータベースに対して加えられた変更を検出し、明らかにする、結果セットの機能です。

同期性のある結果セットは、結果セットがオープンしている間に、基礎となるデータの動的なビューによって、データベースに加えられた変更を見ることができます。結果セットの行の基礎となる列値に加えられた変更を参照できます。

同期性のない結果セットは、基礎となるデータを静的に参照するので、結果セットがオープンしている間にデータベースに加えられた変更に同期しません。データベースに加えられた変更を参照するには、新しい結果セットを取り出す必要があります。

スクロール可能性および同期性の結果セット型

JDBC 2.0機能で結果セットを作成する場合、特定の結果セット型を選択し、結果セットがスクロール可能で位置指定可能かどうか、基礎となるデータベースの変更と同期するかどうかを指定する必要があります。

JDBC 1.0機能のみで十分な場合、JDBC 2.0では、forward-only結果セット型でサポートされます。forward-only結果セットには、同期性は設定できません。

スクロール可能な結果セットが必要な場合、同期性も指定する必要があります。スクロール可能で、基礎となる変更に同期する結果セットには、scroll-sensitive型を指定します。スクロール可能で、基礎となる変更に同期しない結果セットには、scroll-insensitive型を指定します。

要約すると、JDBC 2.0では次の結果セット型が使用可能です。

  • forward-only

    これは、JDBC 1.0の機能です。この型の結果セットは、スクロール不可、位置指定不可および同期性なしです。

  • scroll-sensitive

    この型の結果セットは、スクロール可能で位置指定可能です。また、基礎となるデータベースの変更に同期します。

  • scroll-insensitive

    この型の結果セットは、スクロール可能で位置指定可能ですが、基礎となるデータベースの変更に同期しません。


    注意:

    scroll-sensitive結果セットの同期性は、フェッチ・サイズに影響されます。

更新可能性

更新可能性とは、結果セットのデータを更新し、その変更をデータベースにコピーできる機能を指します。この更新には、結果セットへの新しい行の挿入または既存の行の削除が含まれます。

更新可能性は、基礎となるデータベースへのアクセスを調整するために、データベースの書込みロックを必要とします。同時に複数の書込みロックは使用できないので、結果セットの更新可能性は、データベース・アクセスの同時実行性に関係します。

JDBC 2.0では、オプションで結果セットを更新可能にできます。


注意:

更新可能性はスクロール可能性および同期性から独立しています。ただし、一般に、更新または削除する特定の行に位置指定できるように、更新可能な結果セットはスクロール可能にします。

更新可能性のための同時実行性型

結果セットの同時実行性型によって、更新可能かどうかが判断されます。JDBC 2.0では、次の同時実行性型が使用可能です。

  • 更新可能

    この場合、更新、挿入および削除が結果セットで実行可能で、データベースにコピー可能です。

  • 読取り専用

    結果セットは変更できません。

結果セット・カテゴリの概要

スクロール可能性および同期性は更新可能性から独立しているので、3つの結果セット型と2つの同時実行性型の組合せにより、次に示す合計6つの結果セット・カテゴリができます。

  • forward-only/読取り専用

  • forward-only/更新可能

  • scroll-sensitive/読取り専用

  • scroll-sensitive/更新可能

  • scroll-insensitive/読取り専用

  • scroll-insensitive/更新可能


    注意:

    forward-only更新可能結果セットには、位置指定機能がありません。nextメソッドで反復しながら、行を更新する必要があります。

結果セット拡張のOracle JDBC実装概要

この項では、スクロール可能性(クライアント側キャッシュを使用)および更新可能性(ROWIDを使用)を実現する結果セット拡張のOracle JDBC実装に関する主要な視点について説明します。

ユーザーは、独自のクライアント側キャッシュ・メカニズムを実装できます。そのためのインタフェースが提供されています。

結果セットのスクロール可能性のOracle JDBC実装

基礎となるサーバーがスクロール可能なカーソルをサポートしていないので、スクロール可能性は別のレイヤーでOracle JDBCによって実装する必要があります。

この機能は、スクロール可能な結果セットの行をクライアント側のメモリー・キャッシュに格納することにより、実現されていることに注意してください。


重要:

スクロール可能な結果セットの行はすべて、クライアント側のキャッシュに格納されます。そのため、結果セットに多くの行、多くの列または非常に大きな列が含まれていると、クライアント側のJava Virtual Machine(JVM)に障害が発生する可能性があります。大きな結果セットにはスクロール可能性を指定しないでください。

結果セットの更新可能性のOracle JDBC実装

更新可能性をサポートするために、Oracle JDBCはROWIDを使用して、結果セットに出現するデータベース行を一意に識別します。更新可能な結果セットに問い合せるたびに、選択された列とともに、Oracle JDBCドライバによって自動的にROWIDが取り出されます。


注意:

更新可能性そのものは、クライアント側のキャッシュを必要としません。特に、forward-only更新可能結果セットは、クライアント側のキャッシュを必要としません。

スクロール可能性を実現するカスタム・クライアント側キャッシュの実装

JDBC 2.0のスクロール可能な結果セットをサポートするクライアント側キャッシュを実装する方法には、柔軟性があります。

Oracle JDBCで、完全な実装が提供されますが、独自に実装できるインタフェースOracleResultSetCacheも提供されます。

public interface OracleResultSetCache
{
  /**
   * Save the data in the i-th row and j-th column.
   */
  public void put (int i, int j, Object value) throws IOException;

  /**
   * Return the data stored in the i-th row and j-th column.
   */
  public Object get (int i, int j) throws IOException;

  /**
   * Remove the i-th row.
   */
  public void remove (int i) throws IOException;

  /**
   * Remove the data stored in i-th row and j-th column
   */
  public void remove (int i, int j) throws IOException;

  /**
   * Remove all data from the cache.
   */
  public void clear () throws IOException;

  /**
   * Close the cache.
   */
  public void close () throws IOException;
}

このインタフェースを独自のクラスで実装する場合、アプリケーション・コードでクラスをインスタンス化した後、OracleStatementOraclePreparedStatementまたはOracleCallableStatementオブジェクトのsetResultSetCacheメソッドを使用して、独自の実装を使用するキャッシュ・メカニズムを設定する必要があります。次に、メソッド・シグネチャを示します。

void setResultSetCache(OracleResultSetCache cache) throws SQLException

このメソッドは、問合せを実行する前にコールします。これにより、問合せによって作成される結果セットが、指定のキャッシュ・メカニズムを使用します。

スクロール可能な結果セットまたは更新可能な結果セットの作成

JDBC 2.0結果セット拡張を使用する場合は、問合せを実行する一般的な文を作成するとき、またはプリコンパイルされたSQL文やコール可能文を準備するときに、結果セット型および同時実行性型を指定できます。

この項では、JDBC 2.0拡張を使用する結果セットの作成について説明します。次の項目が含まれます。

結果セットのスクロール可能性および更新可能性の指定

JDBC 2.0では、Connectionクラスに、結果セット型および同時実行性型を入力として取る次のメソッドが追加されています。

  • Statement createStatement(int resultSetType, int resultSetConcurrency)

  • PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)

  • CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency)

作成される文オブジェクトは、適切な種類の結果セットを作成するインテリジェント機能を持ちます。

結果セット型には、次のstatic定数値のうち1つを指定できます。

  • ResultSet.TYPE_FORWARD_ONLY

  • ResultSet.TYPE_SCROLL_INSENSITIVE

  • ResultSet.TYPE_SCROLL_SENSITIVE

同時実行性型には、次のstatic定数値のうち1つを指定できます。

  • ResultSet.CONCUR_READ_ONLY

  • ResultSet.CONCUR_UPDATABLE

StatementPreparedStatementまたはCallableStatementオブジェクトを作成した後、文オブジェクトで次のメソッドをコールすると、文の結果セット型および同時実行性型を検証できます。

  • int getResultSetType() throws SQLException

  • int getResultSetConcurrency() throws SQLException

例17-1 結果セットが指定されたプリコンパイルされたSQL文オブジェクト

次のプリコンパイルされたSQL文オブジェクトの例では、この文で実行される問合せに対してscroll-sensitiveで更新可能な結果セットが指定されます。

...
PreparedStatement pstmt = conn.prepareStatement
  ("SELECT empno, sal FROM emp WHERE empno = ?",
  ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);

pstmt.setString(1, "28959");
ResultSet rs = pstmt.executeQuery();
...

結果セットの制限事項およびダウングレード・ルール

一部の結果セット型は、ある種類の問合せには不適切です。実行する問合せに対して不適切な結果セット型または同時実行性型が指定されている場合、JDBCドライバは次のルール・セットに従い、かわりに使用する最適な型を判断します。

実際の結果セット型および同時実行性型は、文が実行されるときに判断されます。指定された結果セット型または同時実行性型が不適切であれば、文オブジェクトでSQLWarningが発生します。SQLWarningオブジェクトには、要求された型が適切でない理由が含まれます。受け取った結果セットの型が要求した型かどうかを検証するには、警告をチェックします。

結果セットの制限事項

拡張された結果セットの問合せには、次の制限事項が適用されます。次の指針に当てはまらない場合、JDBCドライバによって、代替の結果セット型または同時実行性型が選択されます。

更新可能な結果セットを作成するには、次の制限事項を考慮します。

  • 問合せによって選択できるのは、単一の表のみです。結合操作を含めることはできません。

    さらに、挿入するためには、NULL化可能でない列およびデフォルト値が設定されていない列をすべて、問合せによって選択する必要があります。

  • 問合せではSELECT *を使用できません。

    ただし、回避方法があります。

  • 問合せで選択できるのは、表列のみです。

    導出列や、列の集合のSUMまたはMAXなど、集合体を選択できません。

scroll-sensitive結果セットを作成するには、次の制限事項を考慮します。

  • 問合せではSELECT *を使用できません。

    ただし、回避方法があります。

  • 問合せによって選択できるのは、単一の表のみです。

スクロール可能結果セットおよび更新可能結果セットでは、Streamなどの列を使用できません。サーバーでStream列をフェッチする必要がある場合は、フェッチ・サイズを1に減らし、Stream列の読込みが終了するまで、Stream列に続くすべての列をブロックします。このため、列を一括でフェッチし、スクロールすることはできません。

回避方法

SELECT *の制限事項を回避するには、次の例のように、表の別名を使用します。

SELECT t.* FROM TABLE t ...

注意:

ある問合せから、scroll-sensitiveまたは更新可能な結果セットを作成できるかどうかを判断する簡単な方法があります。ROWID列を問合せリストに追加できる場合は、その問合せからscroll-sensitiveまたは更新可能な結果セットを作成できます。

結果セットのダウングレード・ルール

指定された結果セット型または同時実行性型が不適切な場合、Oracle JDBCドライバは次のルールを使用して、代替型を選択します。

  • 指定した結果セット型がTYPE_SCROLL_SENSITIVEであるにもかかわらず、JDBCドライバがその要求を完全には満たせない場合、JDBCドライバはTYPE_SCROLL_INSENSITIVEにダウングレードしようとします。

  • 指定した結果セット型またはダウングレード後の結果セット型がTYPE_SCROLL_INSENSITIVEであるにもかかわらず、JDBCドライバがその要求を完全には満たせない場合、JDBCドライバはTYPE_FORWARD_ONLYにダウングレードしようとします。

  • 指定した同時実行性型がCONCUR_UPDATABLEであるにもかかわらず、JDBCドライバがその要求を完全には満たせない場合、JDBCドライバはCONCUR_READ_ONLYにダウングレードしようとします。


注意:

JDBCドライバによる結果セット型および同時実行性型の操作は、それぞれ独立しています。

結果セット型および同時実行性型の検証

問合せを実行した後、結果セット・オブジェクトのメソッドをコールすることにより、JDBCドライバが実際に使用した結果セット型と同時実行性型を検証できます。

  • int getType() throws SQLException

    このメソッドは、問合せに対して使用された結果セット型のint値を戻します。ResultSet.TYPE_FORWARD_ONLYResultSet.TYPE_SCROLL_SENSITIVEまたはResultSet.TYPE_SCROLL_INSENSITIVEという値が戻されます。

  • int getConcurrency() throws SQLException

    このメソッドは、問合せに対して使用された同時実行性型のint値を戻します。ResultSet.CONCUR_READ_ONLYまたはResultSet.CONCUR_UPDATABLEという値が戻されます。

スクロール可能な結果セットの位置指定および処理

スクロール可能な結果セットを使用すると、結果セットを前方または後方に反復し、目的の行に位置指定できます。

ここでは、スクロール可能な結果セットの位置指定、およびスクロール可能な結果セットを前方ではなく後方に処理する方法を説明します。次の項があります。

スクロール可能な結果セットの位置指定

スクロール可能な結果セットでは、目的の位置への移動およびカレント位置のチェックを行う結果セット・メソッドを使用できます。


注意:

forward-only結果セットでは、位置を指定できません。位置の指定やカレント位置の判断を試みると、SQLException例外が発生します。

新しい位置へ移動するためのメソッド

スクロール可能な結果セットで新しい位置に移動するには、次の結果セット・メソッドを使用します。

  • void beforeFirst() throws SQLException

    結果セットの最初の行の前に移動します。結果セットに行が含まれていない場合、何もしません。これは、結果セットを反復して前方へ処理する場合の一般的な開始位置で、あらゆる種類の結果セットのデフォルトの開始位置です。

    beforeFirst()をコールすると、結果セット境界の外側に移動します。有効な現在行がなくなるので、この点から相対的に位置を指定できません。

  • void afterLast() throws SQLException

    結果セットの最後の行の後へ移動します。結果セットに行が含まれていない場合、何もしません。これは、結果セットを反復して後方へ処理する場合の一般的な開始位置です。

    afterLast()をコールすると、結果セット境界の外側に移動します。有効な現在行がなくなるので、この点から相対的に位置を指定できません。

  • boolean first() throws SQLException

    結果セットの最初の行に移動します。結果セットに行が含まれていない場合、falseを戻します。

  • boolean last() throws SQLException

    結果セットの最後の行に移動します。結果セットに行が含まれていない場合、falseを戻します。

  • boolean absolute(int row) throws SQLException

    結果セットの先頭または末尾から、絶対行に移動します。正数を入力すると、位置は先頭からになります。負数を入力すると、位置は末尾からになります。結果セットに行が含まれていない場合、falseを戻します。

    結果セットが10行のときにabsolute(11)をコールするなど、最後の行を超えて前方へ移動しようとすると、最後の行の後へ移動します。これは、afterLast()をコールした場合と同じ効果です。

    結果セットが10行のときにabsolute(-11)をコールするなど、最初の行を超えて後方へ移動しようとすると、最初の行の前に移動します。これは、beforeFirst()をコールした場合と同じ効果です。


    注意:

    absolute(1)のコールは、first()のコールと等価です。absolute(-1)のコールは、last()のコールと等価です。

  • boolean relative(int row) throws SQLException

    現在行から相対的な位置に移動します。正数が入力された場合は前方へ、負数が入力された場合は後方へ移動します。結果セットに行が含まれていない場合、falseを戻します。

    relativeメソッドを使用するには、あらかじめ、結果セットの有効な現在行に位置指定しておく必要があります。

    最後の行を超えて前方へ移動しようとすると、最後の行の後へ移動します。これは、afterLast()をコールした場合と同じ効果です。

    最初の行を超えて後方へ移動しようとすると、最初の行の前に移動します。これは、beforeFirst()をコールした場合と同じ効果です。

    relative(0)は有効なコールですが、何もしません。


    注意:

    最初の行の前(デフォルトの開始位置)または最後の行の後から相対的な位置を指定することはできません。これらの位置から相対的な位置指定をしようとすると、SQLException例外が発生します。

カレント位置をチェックするメソッド

スクロール可能な結果セットでカレント位置をチェックするには、次の結果セット・メソッドを使用します。

  • boolean isBeforeFirst() throws SQLException

    位置が最初の行の前であれば、trueを戻します。

  • boolean isAfterLast() throws SQLException

    位置が最後の行の後であれば、trueを戻します。

  • boolean isFirst() throws SQLException

    位置が最初の行であれば、trueを戻します。

  • boolean isLast() throws SQLException

    位置が最後の行であれば、trueを戻します。

  • int getRow() throws SQLException

    現在行の行番号を戻します。有効な現在行がない場合、0(ゼロ)を戻します。


    注意:

    booleanメソッド(isFirst()isLast()isAfterFirst()およびisAfterLast())はすべて、falseを戻します。また、結果セットに行が含まれていない場合も、例外は発生しません。

スクロール可能な結果セットの処理

スクロール可能な結果セットでは、結果セットを処理するときに、前方ではなく後方へ反復できます。次のメソッドが使用可能です。

  • boolean next() throws SQLException

  • boolean previous() throws SQLException

previous()メソッドの動作は、next()メソッドと似ています。新しい現在行が有効であればtrueを戻し、行が終了すると(最初の行を超えると)falseを戻します。

後方への処理と前方への処理

next()メソッドを使用して、結果セット全体を前方に向かって処理できます。結果セットのデフォルトの開始位置は、最初の行の前です。結果セットが作成された後、他の位置に移動した場合、beforeFirst()メソッドをコールして戻れます。

結果セット全体を後方に向かって処理するには、afterLast()をコールし、previous()メソッドを使用します。たとえば、次のようになります。

...
/* NOTE: The specified concurrency type, CONCUR_UPDATABLE, is not relevant to this example. */

Statement stmt = conn.createStatement
         (ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);

ResultSet rs = stmt.executeQuery("SELECT empno, sal FROM emp");

rs.afterLast();
while (rs.previous())
{
   System.out.println(rs.getString("empno") + " " + rs.getFloat("sal"));
}
...

相対的な位置指定とは異なり、next()は最初の行の前から、previous()は最後の行の後から使用できます。これらのメソッドを使用するとき、有効な現在行に位置指定しておく必要はありません。


注意:

スクロール可能でない結果セットで実行できるのは、next()メソッドによる処理のみです。previous()メソッドを使用しようとすると、SQLException例外が発生します。

フェッチ方向のプリセット

JDBC 2.0標準では、結果セットの処理に使用する方向をあらかじめ指定できます。この方向を、フェッチ方向と呼びます。これにより、JDBCドライバは処理を最適化できます。次の結果セット・メソッドが指定されています。

  • void setFetchDirection(int direction) throws SQLException

  • int getFetchDirection() throws SQLException

Oracle JDBCドライバは前方へのプリセット値のみをサポートします。これは、ResultSet.FETCH_FORWARD静的定数値を入力することにより、指定できます。

ResultSet.FETCH_REVERSEおよびResultSet.FETCH_UNKNOWNという値はサポートされていません。指定しようとすると、SQL警告が発生し、設定は無視されます。

結果セットの更新

同時実行性型CONCUR_UPDATABLEを使用すると、結果セットの行の更新、削除または挿入ができます。

UPDATEまたはINSERT操作を結果セットで実行した後、別処理で変更をデータベースに伝播させます。変更を取り消すには、この処理をスキップします。

しかし、結果セットでのDELETE操作は、データベースにも即時に実行されます(ただし、必ずしもコミットはされません)。


注意:

更新可能な結果セットを使用する場合、一般的にはスクロール可能にもします。これにより、変更する行に移動できます。forward-only更新可能な結果セットの場合、next()メソッドで反復しながら、行を更新する必要があります。

この項には、次の項目が含まれます。

結果セットでのDELETE操作実行

結果セットのdeleteRow()メソッドは、現在行を削除します。次に、メソッド・シグネチャを示します。

void deleteRow() throws SQLException

注意:

結果セットのUPDATEおよびINSERT操作では、変更をデータベースに伝播させる別処理が必要ですが、結果セットのDELETE操作は、データベースの対応する行にも即時に実行されます。

deleteRowメソッドをコールすると、変更は、次のトランザクションCOMMIT操作で確定します。デフォルトでは、自動コミット・フラグはtrueに設定されていることにも注意してください。つまり、このデフォルトをオーバーライドしなければ、deleteRowメソッドの操作は即時に実行され、コミットされます。


結果セットはスクロール可能でもあるとします。使用可能な位置指定メソッド(有効な現在行に移動しないbeforeFirstメソッドとafterLastメソッドは除く)を使用して行の位置を指定し、その行を削除できます。次に例を示します。

...
rs.absolute(5);
rs.deleteRow();
...

重要:

削除された行はデータベースから削除された後も、結果セット・オブジェクトに残ります。

これに対して、スクロール可能結果セットでは、ローカルな結果セット・オブジェクトにおけるDELETE操作は理解が容易です。DELETEの後、行は結果セットに存在しません。削除された行の前の行が現在行になり、後続の行の行番号が変更されます。


結果セットでのUPDATE操作実行

結果セットのUPDATE操作を実行するには、2つの別々の処理が必要です。まず、結果セットのデータを更新し、次に、データベースに変更をコピーします。

結果セットはスクロール可能でもあるとします。使用可能な位置指定メソッド(有効な現在行に移動しないbeforeFirst()afterLast()は除く)を使用して行の位置を指定し、その行を必要に応じて更新できます。

結果セットとデータベースの行を更新する手順を示します。

  1. 適切なupdateXXXメソッドをコールし、変更する列のデータを更新します。

    JDBC 2.0では、前に使用可能だったデータベースを直接更新するsetXXXメソッドと同じように、結果セット・オブジェクトに各データ型のupdateXXXメソッドがあります。

    これらのメソッドは、列番号のintまたは列名の文字列と、適切なデータ型の項目を取り、新しい値を設定します。結果セットrsの例をいくつか示します。

    rs.updateString(1, "mystring");
    rs.updateFloat(2, 10000.0f);
    
  2. updateRowメソッドをコールして、変更をデータベースにコピーするか、cancelRowUpdatesメソッドをコールして、変更を取り消します。

    updateRowメソッドをコールすると、変更は実行され、次のトランザクションCOMMIT操作で確定します。デフォルトでは、自動コミット・フラグはtrueに設定されているので、実行された操作は即時コミットされることに注意してください。

    データベースにコピーする前に変更を取り消すには、かわりにcancelRowUpdatesメソッドをコールします。これにより、ローカルな結果セット・オブジェクトの対応する行も、元の値に回復します。updateRowメソッドをコールすると、変更はトランザクションに書き込まれます。取り消すには、トランザクションをロールバックする必要があることに注意してください。


    注意:

    ROLLBACK操作を行うには、自動コミットを無効にしておく必要があります。

    updateRowをコールする前に別の行に移動しても、変更は取り消され、結果セットは元の値に回復します。

    updateRowをコールする前に、通常のgetXXXメソッドをコールし、値が正しく更新されたかどうかを検証できます。これらのメソッドは、入力としてint列索引または文字列の列名を取ります。たとえば、次のようになります。

    float myfloat = rs.getFloat(2);
    ...
    // process myfloat to see if it's appropriate
    ...
    

    注意:

    結果セットのUPDATE操作は、すべての結果セット型(forward-only、scroll-sensitiveおよびscroll-insensitive)のローカルな結果セット・オブジェクトで参照できます。

データベースにもコピーされる、結果セットのUPDATE操作の例を示します。10番目の行が更新されます。列番号を使用して列1を指定し、列名salを使用して列2を指定します。

Statement stmt = conn.createStatement
         (ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
ResultSet rs = stmt.executeQuery("SELECT empno, sal FROM emp");
if (rs.absolute(10))        // (returns false if row does not exist)
{
   rs.updateString(1, "28959");
   rs.updateFloat("sal", 100000.0f);
   rs.updateRow();
}
// Changes are made permanent with the next COMMIT operation.

結果セットでのINSERT操作実行

結果セットのINSERT操作では、結果セットの挿入行と呼ばれるものが使用されます。これは、データベースにコピーされるまで、挿入する行のデータを保持するステージング領域です。この行に明示的に移動し、挿入するデータを書き込む必要があります。

UPDATE操作と同様に、結果セットのINSERT操作には、まずデータを挿入行に書き込み、次にデータベースにコピーする、別々の手順が必要です。

結果セットのINSERT操作を実行する手順を示します。

  1. 結果セットのmoveToInsertRowメソッドをコールして、挿入行に移動します。


    注意:

    moveToInsertRowをコールする前のカレント位置は、結果セットによって記憶されます。後で、moveToCurrentRowをコールすると、カレント位置に戻れます。

  2. UPDATE操作と同様に、適切なupdateXXXメソッドを使用し、データを列に書き込みます。たとえば、次のようになります。

    rs.updateString(1, "mystring");
    rs.updateFloat(2, 10000.0f);
    

    列番号の整数を指定するかわりに、列名の文字列を指定できます。


    重要:

    挿入行の各列の値は、その列に対してupdateXXXメソッドをコールするまで、未定義です。このメソッドをコールして、NULL化可能でない列すべてに非NULL値を指定する必要があります。指定せずに行をデータベースにコピーしようとすると、SQLException例外が発生します。

    ただし、NULL化可能な列に対しては、updateXXXをコールしなくてもかまいません。この場合、値はNULLになります。


  3. 結果セットのinsertRowメソッドをコールして、変更をデータベースにコピーします。

    insertRowをコールすると、挿入は処理され、次のトランザクションCOMMIT操作で確定します。

    insertRowをコールする前に別の行に移動すると、挿入は取り消され、挿入行はクリアされます。

    insertRowをコールする前に、通常のgetXXXメソッドをコールし、値が正しく挿入行に設定されたかどうかを検証できます。これらのメソッドは、入力としてint列索引または文字列の列名を取ります。たとえば、次のようになります。

    float myfloat = rs.getFloat(2);
    ...process myfloat to see if it's appropriate...
    

    注意:

    どの結果セット型でも、結果セットのINSERT操作で挿入された行を参照できません。

次の例では、結果セットのINSERT操作を実行します。挿入行に移動し、データを書き込み、データをデータベースにコピーし、挿入行に移動する前に現在行だった位置に戻ります。列番号を使用して列1を指定し、列名salを使用して列2を指定します。

...
Statement stmt = conn.createStatement
         (ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);

ResultSet rs = stmt.executeQuery("SELECT empno, sal FROM emp");

rs.moveToInsertRow();
rs.updateString(1, "28959");
rs.updateFloat("sal", 100000.0f);
rs.insertRow();
// Changes will be made permanent with the next COMMIT operation.
rs.moveToCurrentRow();  // Go back to where we came from...
...

更新の競合回避

JDBCドライバで使用される更新可能な結果セットに関しては、次の事項に注意してください。

  • ドライバは、更新可能な結果セットに対する書込みロックを施行しません。

  • ドライバは、結果セットのDELETEまたはUPDATE操作の競合をチェックしません。

DELETEまたはUPDATE操作を、コミットされた別のトランザクションによって更新された行に対して実行すると、競合が発生します。

Oracle JDBCドライバは、ROWIDを使用し、データベース表の行を一意に識別します。ドライバがUPDATEまたはDELETE操作をデータベースに送信しようとしたとき、ROWIDが有効であれば、操作は実行されます。

コミットされた別のトランザクションによる変更は、レポートされません。競合は警告なしに無視され、新しい変更によって前の変更は上書きされます。

競合を回避するには、結果セットを作成する問合せを実行するときに、OracleのFOR UPDATE機能を使用します。これにより、競合を回避できますが、データへの同時アクセスも禁止されます。データ項目に同時に設定できる書込みロックは1つのみです。

フェッチ・サイズ

デフォルトでは、Oracle JDBCは問合せを実行するとき、データベース・カーソルから一度に10行の結果セットを受け取ります。これが、デフォルトのOracle行フェッチ・サイズ値です。データベース・カーソルへの1回のラウンドトリップで取り出される行の数を変更するには、行フェッチ・サイズ値を変更します。

JDBC 2.0では、1回のデータベース・ラウンドトリップでフェッチされる行の数を、問合せごとに指定することもできます。この数をフェッチ・サイズと呼びます。Oracle JDBCでは、文オブジェクトのデフォルト・フェッチ・サイズとして行プリフェッチ値が使用されます。フェッチ・サイズを設定すると、行プリフェッチ設定がオーバーライドされ、その文オブジェクトで実行される後続の問合せに影響を与えます。

フェッチ・サイズは、結果セットでも使用されます。文オブジェクトが問合せを実行するとき、文オブジェクトのフェッチ・サイズが、その問合せで作成される結果セット・オブジェクトに渡されます。ただし、結果セット・オブジェクトでフェッチ・サイズを設定して、渡された文フェッチ・サイズをオーバーライドすることもできます。


注意:

結果セットが作成された後で文オブジェクトのフェッチ・サイズを変更しても、結果セットには影響を与えません。

明示的に設定された場合でも、渡された文フェッチ・サイズと等しいデフォルトでも、結果セット・フェッチ・サイズによって、その結果セットでの後続のデータベースへのラウンドトリップで取り出される行の数が判断されます。このラウンドトリップには、元の問合せを完了するために必要なラウンドトリップと、結果セットへのデータの再フェッチに必要なラウンドトリップが含まれます。scroll-sensitiveまたはscroll-insensitive/更新可能な結果セットを更新するために、データは明示的または暗黙的に再フェッチされる可能性があります。

フェッチ・サイズの設定

StatementPreparedStatementCallableStatementおよびResultSetオブジェクトのすべてで、フェッチ・サイズを設定および取り出す次のメソッドが使用可能です。

  • void setFetchSize(int rows) throws SQLException

  • int getFetchSize() throws SQLException

問合せのフェッチ・サイズを設定するには、問合せを実行する前に文オブジェクトのsetFetchSizeをコールします。フェッチ・サイズをNに設定すると、1回のデータベースへのラウンドトリップで、N行がフェッチされます。

問合せを実行した後、結果セット・オブジェクトのsetFetchSizeをコールすると、文オブジェクト・フェッチ・サイズによって、渡された文オブジェクト・フェッチ・サイズがオーバーライドされます。これは、データベースへの後続のラウンドトリップに影響を与え、元の問合せに対してより多くの行が取り出されます。後で実行される他の行の再フェッチにも影響を与えます。

行の再フェッチ

結果セットのいくつかの型で、データを再フェッチする結果セットのrefreshRowメソッドがサポートされています。このメソッドは、データベースに戻って、結果セットの現在行から始まるn行に対応するデータベースの行を再度取り出します。nはフェッチ・サイズです。これにより、外側のトランザクションの分離レベルに依存しますが、操作中の結果セットの外で実行されたデータベースに対する最新の更新を参照できます。

再フェッチによって再度取り出されるのは、すでに結果セットにある行に対応する行のみです。元の問合せの後、データベースで挿入または削除された行に対しては、何もしません。挿入された行は無視されます。行がデータベースから削除された後も、対応する行は結果セットに残ります。データベースで削除された行を再フェッチしようとした場合、結果セットの対応する行には、元の値が保存されます。


注意:

いくつかの基準を持つ問合せに基づいてTYPE_SCROLL_SENSITIVE結果セットを宣言してから、列の値が問合せの基準に一致しなくなるように外部的に行を更新すると、ドライバは、行がデータベースから削除され、送信された問合せでは取り出せないかのように動作します。このため、refreshRowメソッドをコールしても個別行の更新は確認できません。

refreshRowメソッドのシグネチャを示します。

void refreshRow() throws SQLException

このメソッドをコールするときは、行境界の外側や挿入行ではなく、有効な現在行に位置指定しておく必要があります。

次の結果セットのカテゴリで、refreshRowメソッドがサポートされています。


注意:

Scroll-Sensitive結果セット機能は、refreshRowの暗黙的なコールによって実装されます。

内部的および外部的に加えられたデータベース変更の参照

この項では、次の事項を参照する結果セットの機能について説明します。


注意:

Sun社のJDBC 2.0の仕様では、外部変更は「他からの変更」と呼ばれています。

この項には、次の項目が含まれます。

内部変更の参照

更新可能な結果セットの、結果セット自体の変更を参照する機能は、結果セット型と変更の種類の両方に依存します。要約すると次のようになります。

  • 内部的なDELETE操作は、スクロール可能な結果セットでは参照できますが、forward-only結果セットでは参照できません。

    スクロール可能な結果セットの行を削除すると、前の行が新しい現在行になり、後続の行の行番号が更新されます。

  • 内部的なUPDATE操作は、結果セット型にかかわらず、常に参照できます。

  • 内部的なINSERT操作は、結果セット型にかかわらず、常に参照できません。

内部変更が参照である場合は、本質的には、同じデータ項目の前のupdateXXXコールによって変更されたデータを、後続のgetXXXコールが参照できるという意味です。

JDBC 2.0のDatabaseMetaDataオブジェクトには、これを検証する次のメソッドが含まれています。

  • boolean ownDeletesAreVisible(int) throws SQLException

  • boolean ownUpdatesAreVisible(int) throws SQLException

  • boolean ownInsertsAreVisible(int) throws SQLException

いずれも、結果セット型ResultSet.TYPE_FORWARD_ONLYResultSet.TYPE_SCROLL_SENSITIVEまたはResultSet.TYPE_SCROLL_INSENSITIVEを入力として受け取ります。


注意:

実行のトリガーの原因になる内部変更を行った場合、トリガーによる変更は外部変更の効果を持ちます。ただし、トリガーが更新中の行のデータに影響を与える場合、スクロール可能または更新可能な結果セットでは、これらの変更は参照されます。更新の後、暗黙的な行の再フェッチが発生するためです。

外部変更の参照

基礎となるデータベースに対する外部変更は、scroll-sensitive結果セットでのみ参照でき、参照の対象は、外部的なUPDATE操作による変更のみです。外部的なDELETEまたはINSERT操作による変更は、参照できません。


注意:

外部からの変更の参照に関する説明では、外側のトランザクションには、変更を参照できるように、トランザクション自体の分離レベルが設定されているものとします。

JDBC 2.0のDatabaseMetaDataオブジェクトには、これを検証する次のメソッドが含まれています。

  • boolean othersDeletesAreVisible(int) throws SQLException

  • boolean othersUpdatesAreVisible(int) throws SQLException

  • boolean othersInsertsAreVisible(int) throws SQLException

いずれも、結果セット型ResultSet.TYPE_FORWARD_ONLYResultSet.TYPE_SCROLL_SENSITIVEまたはResultSet.TYPE_SCROLL_INSENSITIVEを入力として受け取ります。


注意:

refreshRowメソッドの明示的な使用は、この可視性の説明とは別です。たとえば、外部更新がscroll-insensitive結果セットにおいても不可視であったとしても、scroll-insensitive/更新可能な結果セットで行を明示的に再フェッチすることで、実行された外部変更を取り出すことができます。ここでいう可視性とは、scroll-insensitive/更新可能な結果セットでは、このような変更を自動的に、また、暗黙的には参照しないという文脈においてのみ使用しています。

外部変更の可視性と検出

外部ソースによって基礎となるデータベースに加えられる変更に関して、2つの概念があります。これらは似ていますが、ローカルな結果セットからの変更の可視性という観点から見ると異なります。

  • 変更の可視性

  • 変更の検出

変更が「可視」であるとは、結果セットの行を見たとき、外部ソースによってデータベースの対応する行に加えられた変更から得られる新しいデータ値を参照できるという意味です。

一方、変更が「検出される」とは、結果セットに最初に値が移入されて以降の新しい値であることを、結果セットが認識するという意味です。

Oracle結果セットが新しいデータを参照したときでも(scroll-sensitive結果セットでの外部UPDATEのように)、このデータが結果セットが移入された後で変更されたことは認識されません。このような変更は、検出されません。

JDBC 2.0のDatabaseMetaDataオブジェクトには、これを検証する次のメソッドが含まれています。

  • boolean deletesAreDetected(int) throws SQLException

  • boolean updatesAreDetected(int) throws SQLException

  • boolean insertsAreDetected(int) throws SQLException

いずれも、結果セット型ResultSet.TYPE_FORWARD_ONLYResultSet.TYPE_SCROLL_SENSITIVEまたはResultSet.TYPE_SCROLL_INSENSITIVEを入力として受け取ります。

したがって、JDBC 2.0で指定されている、変更を検出する結果セット・メソッド(rowDeletedrowUpdatedおよびrowInserted)は、常にfalseを戻します。これらをコールする意味はありません。

内部変更および外部変更の可視性のサマリー

表17-1で、Oracle JDBC実装の結果セット・オブジェクトが、結果セット自体が内部的に実行した変更および基礎となるデータベースに対してユーザー自身のトランザクション、またはその他のコミットされたトランザクションから外部的に実行された変更を参照できるかどうかに関する、前の項の説明を要約します。

表17-1 Oracle JDBCでの内部変更および外部変更の可視性

結果セット型 内部的なDELETEの参照が可能か 内部的なUPDATEの参照が可能か 内部的なINSERTの参照が可能か 外部的なDELETEの参照が可能か 外部的なUPDATEの参照が可能か 外部的なINSERTの参照が可能か

forward-only

不可

不可

不可

不可

不可

scroll-sensitive

不可

不可

不可

scroll-insensitive

不可

不可

不可

不可



注意:

  • refreshRowメソッドの明示的な使用は、外部変更の可視性の概念とは別です。

  • scroll-sensitive結果セットを基礎とするUPDATE操作のように、外部変更が可視であるときでも、これらは検出されません。結果セットのrowDeletedrowUpdatedおよびrowInsertedメソッドは常にfalseを戻します。


Scroll-Sensitive結果セットのOracle実装

scroll-sensitive結果セットのOracle実装には、ウィンドウの概念が関係します。フェッチ・サイズの基準は、ウィンドウ・サイズです。ウィンドウ・サイズが、結果セットの行を更新する頻度に影響を与えます。

指定された行に移動して現在行を確立すると、ウィンドウには、結果セットの現在行から始まるn行が含められます。nは、結果セットで使用されているフェッチ・サイズです。結果セットが最初に作成されたときには、現在行がないので、ウィンドウもありません。デフォルトの位置は最初の行の前で、有効な現在行ではありません。

行を移動するとき、現在行がウィンドウ内にある間は、ウィンドウは変更されません。しかし、ウィンドウの外にある新しい現在行に移動すると、新しい現在行から始まるN行のウィンドウが再定義されます。

ウィンドウが再定義されると、refreshRowメソッドが暗黙的にコールされ、新しいウィンドウの行に対応するデータベースのN行が自動的に再フェッチされます。これにより、新しいウィンドウのデータが更新されます。

そのため、外部更新は、scroll-sensitive結果セットですぐには参照できません。前述のように、自動再フェッチの後で参照できるようになります。


注意:

この種類の再フェッチは効率が高くなく、最適化された方法でもないので、重大なパフォーマンスの問題になります。現在実装されているようなscroll-sensitive結果セットを使用する前に、十分検討してください。同期性とパフォーマンスの間にも、重大なトレードオフがあります。ほとんどの同期性のある結果セットは、フェッチ・サイズが1です。この場合、行を移動するたびに、新しい現在行が再フェッチされます。これは、アプリケーションのパフォーマンスに、重大な影響を与えます。