ヘッダーをスキップ
Oracle® Fusion Middleware Oracle WebLogic Server JDBC プログラマーズ ガイド
11g リリース 1 (10.3.1)
B55535-01
 

目次
目次

戻る
戻る
 
次へ
次へ
 

6 WebLogic Server における RowSet の使い方

以下の節では、WebLogic RowSet の特徴と使い方について説明します。

RowSet について

WebLogic Server には、JSR-114 の仕様に準拠した Java RowSet が実装されています。この仕様の詳細については、Sun の Web サイト (http://java.sun.com/products/jdbc/download.html) を参照してください。WebLogic の RowSet 実装には、RowSet 仕様の拡張も含まれています。こうした拡張を使用すると、アプリケーションで RowSet をより有効に活用できます。

RowSet は Java ResultSet の拡張です。ResultSet と同様、RowSet も表形式のデータを保持する Java オブジェクトです。ただし、RowSet では ResultSet 機能に大幅な柔軟性が加えられていて、ResultSet の持つ制限の一部が緩和されたり削減されたりしています。

RowSet の種類

WebLogic Server の RowSet 実装には、以下のような種類とユーティリティがあります。

標準の RowSet :

WebLogic の RowSet 拡張 :

RowSet を使用したプログラミング

WebLogic Server の RowSet 実装は、ユーザが以下の手順で RowSet を扱うことを想定して設計されています。

  1. RowSet を作成してコンフィグレーションします (クエリ、データベース接続、その他のプロパティを定義します)。

  2. RowSet にデータを入力します (クエリ パラメータを指定してクエリを実行します)。

  3. 必要に応じて RowSet メタデータを指定します。

  4. 必要に応じて RowSet にフィルタまたはソーターを設定します。

  5. RowSet 内のデータを操作します (挿入、更新、削除を行います)。

  6. RowSet からデータベースへのデータ変更の同期を行います。

変更を同期した後には、アプリケーションの設計に応じて手順 2 または手順 3 から繰り返し処理を行えます。「トランザクション完了後の WebLogic RowSet の再利用」を参照してください。

WebLogic の RowSet 実装にはライフ サイクルのフレームワークがあり、これによって RowSet オブジェクトが異常な状態にならないようにしています。WebLogic Server では、上記の処理段階に応じて、内部的に RowSet に対しライフ サイクルのステージが設定されます。データが失われるリスクを減らすため、ライフ サイクルのステージに応じて RowSet に実施できる操作が制限されます。たとえば RowSet のステージが「Updating」(更新) の場合、RowSet に対して呼び出せるのは、updateXXX() メソッド (updateString()updateInt() など) のみです。この制限は、updateRow() を呼び出して更新のフェーズが完了するまで適用されます。

重要な注意事項を以下に示します。

RowSet の作成時からデータ変更がデータベースとの間で同期されるまでの RowSet のライフ サイクルのステージの実例については、コード リスト 6-1 のコメントを参照してください。

CachedRowSet

以下の節では、WebLogic Server における標準の CachedRowSet の使い方について説明します。

また、標準の CachedRowSet オブジェクトの WebLogic 拡張の使用方法については、「WLCachedRowSet」を参照してください。

特性

CachedRowSet は接続されていない ResultSet オブジェクトです。CachedRowSet のデータはメモリに格納されます。WebLogic Server 実装の CachedRowSet には以下の特性があります。

  • データの挿入、更新、削除に使用できる。

  • シリアライズ可能なので、シン クライアントや無線デバイスなどのさまざまなアプリケーション コンポーネントに渡すことができる。

  • トランザクション処理に対応しているため RowSet を再利用できる。「トランザクション完了後の WebLogic RowSet の再利用」を参照してください。

  • RowSet でのデータ変更をデータベースとの間で同期するためにオプティミスティックな同時実行性の制御が使用される。

  • SyncProvider 例外から得られる SyncResolver オブジェクトを使用して、RowSet でのデータ変更とデータベース間の衝突を解決する。「SyncResolver を使用した SyncProviderException の処理」を参照してください。

CachedRowSet に特有のプログラミング上の考慮事項と制限

アプリケーションの設計時には、次のことを考慮するようにします。

  • RowSet のクエリ結果はすべてメモリに格納される

  • データの競合

RowSet のクエリ結果はすべてメモリに格納される

CachedRowSet ではデータベースへの接続が保持されないので、すべてのクエリの結果は必ずメモリに格納されます。クエリの結果が非常に大きいと、メモリ不足エラーによりパフォーマンスが低下することがあります。大きなデータ セットの場合、ResultSet を使用する方が適していることもあります。ResultSet ではデータベースへの接続が保持されるため、クエリ結果の一部をメモリに保持してから必要に応じてデータベースに戻り、さらに行を取得できるためです。

データの競合

CachedRowSet は、RowSet への入力から RowSet 内のデータ変更がデータベースとの間で同期されるまでの間に、別のプロセスで更新されることがあまりないようなデータに使用するのがもっとも適しています。この期間にデータベースが変更されるとデータの競合が発生します。データの競合を検出して処理する方法については、「SyncResolver を使用した SyncProviderException の処理」を参照してください。

サンプル コード

コード リスト 6-1 に CachedRowSet の基本的なワークフローを示します。このコード リストでは、主要な各操作と対応する RowSet のライフ サイクルのステージについてコメントを使って説明します。このサンプル コードの後で、サンプルの主要な各セクションについてさらに詳細に説明します。

コード リスト 6-1 CachedRowSet のサンプル コード

import javax.sql.rowset.CachedRowSet;
import weblogic.jdbc.rowset.RowSetFactory; 
public class CachedRowSetDemo {
public static void main (String[] args) {
// 「DESIGNING」(設計) ライフサイクル ステージ - RowSet を作成してプロパティを設定する
  try {
    // RowSetFactory インスタンスを作成する
    RowSetFactory rsfact = RowSetFactory.newInstance();
    CachedRowSet rs = rsfact.newCachedRowSet();
    // DataSource を介してデータベース アクセスを設定する
    rs.setDataSourceName(examples-dataSource-demoPool);
    // 他のオプションについては、「データベース接続のオプション」を参照
    // クエリ コマンドを設定する
    rs.setCommand("SELECT ID, FIRST_NAME, MIDDLE_NAME, LAST_NAME,
    PHONE, EMAIL FROM PHYSICIAN WHERE ID>?");
   // 「CONFIGURE QUERY」(クエリのコンフィグレーション) ライフサイクル操作
    rs.setInt(1, 0); 
    // 「POPULATING」(入力) ライフサイクル ステージ - RowSet に入力するためのコマンドを実行する
    rs.execute();
   }
   // 「CONFIGURING METADATA」(メタデータのコンフィグレーション) - メタデータを入力してから設定する
   // これには KeyColumn を含む
    rs.setKeyColumns(new int[] { 1 });
    while (rs.next ()) // 「NAVIGATING」(案内) ライフサイクル ステージ
      {
      System.out.println ("ID: " +rs.getInt (1));
      System.out.println ("FIRST_NAME: " +rs.getString (2));
      System.out.println ("MIDDLE_NAME: " +rs.getString (3));
      System.out.println ("LAST_NAME: " +rs.getString (4));
      System.out.println ("PHONE: " +rs.getString (5));
      System.out.println ("EMAIL: " +rs.getString (6));
      }
   }
// データを操作する
// RowSet の行を削除する
  try {
  // 「MANIPULATING」(操作) ライフサイクル ステージ - 行に移動する
  // (カーソルを手動で移動する)
  rs.last();
  rs.deleteRow();
  // データベースはまだ更新されていないことに注意
  }  
// RowSet の行を更新する
  try {
  // 「MANIPULATING」(操作) ライフサイクル ステージ - 行に移動する
  // (カーソルを手動で移動する)
  rs.first();
  // 「UPDATING」(更新) ライフサイクル ステージ - update() メソッドを呼び出す
  rs.updateString(4, "Francis");
  // 「MANIPULATING」(操作) ライフサイクル ステージ - 更新を完了する
  rs.updateRow();
  // データベースはまだ更新されていないことに注意
  }  
// 「INSERTING」(挿入) ライフサイクル ステージ - RowSet に行を挿入する
  try {
    rs.moveToInsertRow();
    rs.updateInt(1, 104);
    rs.updateString("FIRST_NAME", "Yuri");
    rs.updateString("MIDDLE_NAME", "M");
    rs.updateString("LAST_NAME", "Zhivago");
    rs.updateString("PHONE", "1234567812");
    rs.updateString("EMAIL", "Yuri@poet.com");
    rs.insertRow(); // 「更新の完了」アクション
    // 「MANIPULATING」(操作) ライフサイクル ステージ - 行に移動する
    rs.moveToCurrentRow();
    // データベースはまだ更新されていないことに注意
  }  
// すべての変更 (削除、更新、挿入) をデータベースに送信する
// 「DESIGNING」(設計) または「POPULATING」(入力) ライフサイクル ステージ - データベースとの間で変更を同期した後は
// 他の環境設定に応じたライフサイクル ステージになる
// 「トランザクション完了後の WebLogic RowSet の再利用」を参照
  try {
    rs.acceptChanges();
    rs.close();
   }
}

CachedRowSet のクラスとインタフェースのインポート

標準の RowSet を使用するには、次のクラスをインポートする必要があります。

javax.sql.rowset.CachedRowSet;
weblogic.jdbc.rowset.RowSetFactory;

CachedRowSet の作成

RowSet はファクトリ インタフェースから作成されます。WebLogic Server で RowSet を作成するには、主に以下の手順に従います。

  1. RowSetFactory インスタンスを作成します。このインスタンスはアプリケーションで使用する RowSet オブジェクトを作成するためのファクトリとして機能します。RowSetFactory のデータベース接続のプロパティを指定すると、同じデータベース接続を使用する複数の RowSet を少ないコード行で作成できます。

    RowSetFactory rsfact = RowSetFactory.newInstance();
    
  2. WLCachedRowSet を作成して javax.sql.rowset.CachedRowSet オブジェクト型にキャストします。デフォルトでは WebLogic newCachedRowSet() RowSetFactory メソッドで WLCachedRowSet オブジェクトが作成されます。このオブジェクトをそのまま使用することもできますが、標準の CachedRowSet オブジェクトを使用する場合は、WLCachedRowSet オブジェクトを CachedRowSet オブジェクトにキャストできます。

    CachedRowSet rs = rsfact.newCachedRowSet();
    

CachedRowSet のプロパティの設定

RowSet には、同時方式の種類、データ ソースの名前、トランザクション アイソレーション レベルなど数多くのプロパティがあります。これらのプロパティを設定して RowSet の動作を決定できます。RowSet の特定の用途に応じて必要なプロパティのみを設定する必要があります。利用可能なプロパティについては、http://java.sun.com/javase/6/docs/api/javax/sql/rowset/BaseRowSet.htmljavax.sql.rowset.BaseRowSet クラスの Javadoc を参照してください。

データベース接続のオプション

ほとんどのアプリケーションでは、データベースからのデータを RowSet に入力します。RowSet とデータベースとの接続は、以下のいずれかの方法で設定します。

  • データ ソースを使用して自動的に接続する - setDataSourceName() メソッドを使用して、JDBC データ ソースの JNDI 名を指定できます。execute() および acceptChanges() を呼び出すと、RowSet でデータ ソースからのデータベース接続が取得され、それが使用されて、データ ソースの接続のプールに戻されます。これは、推奨される方法です。

    rs.setDataSourceName(examples-dataSource-demoPool);
    
  • データベース接続を手動で取得する - RowSet で必要になる前にアプリケーションでデータベース接続を取得してから、接続オブジェクトを execute() メソッドおよび acceptChanges() メソッドのパラメータとして渡します。必要に応じて接続を閉じる必要もあります。

    // DataSource をルックアップして接続を取得する
    ctx = new InitialContext(ht);
    javax.sql.DataSource ds = (javax.sql.DataSource) ctx.lookup ("myDS");
    conn = ds.getConnection();
    
    // 接続を RowSet に渡す
    rs.execute(conn); 
    

    JDBC データ ソースの詳細については、「DataSource オブジェクトからのデータベース接続の取得」を参照してください。

  • JDBC ドライバをロードして直接的に接続する - JDBC ドライバをロードして適切なプロパティを設定すると、RowSet では execute() および acceptChanges() を呼び出したときにデータベース接続が作成されます。RowSet では接続を使用し終えるとすぐにその接続が閉じられます。RowSet では execute() メソッド呼び出しと acceptChanges() メソッド呼び出しの間、接続は保持されません。

    Class.forName("com.pointbase.jdbc.jdbcUniversalDriver");
    rs.setUrl("jdbc:pointbase:server://localhost/demo");
    rs.setUsername("examples");
    rs.setPassword("examples");
    rs.execute();
    
  • RowSetFactory の接続のプロパティを設定する - RowSetFactory のデータベース接続のプロパティを設定すると、RowSetFactory から作成されたすべての RowSet でその接続のプロパティが継承されます。データ ソースをルックアップしてから setDataSource() メソッドで RowSetFactory のデータ ソースのプロパティを設定するのが、推奨される方法です。

    // DataSource をルックアップして接続を取得する
    ctx = new InitialContext(ht);
    javax.sql.DataSource ds = (javax.sql.DataSource) ctx.lookup ("myDS");
    
    // RowSetFactory にデータ ソースのプロパティを設定する
    rsfact.setDataSource(ds);
    

CachedRowSet への入力

RowSet への「Populating」(入力) とは、RowSet にデータの行を格納する作業のことをいいます。通常、このデータの取得元はリレーショナル データベースです。データベースからのデータは、以下のいずれかの方法で RowSet に入力できます。

  • setCommand() メソッドで SQL コマンドを設定してから、execute() メソッドでそのコマンドを実行する。

    rs.setCommand("SELECT ID, FIRST_NAME, MIDDLE_NAME, LAST_NAME,
        PHONE, EMAIL FROM PHYSICIAN");
    rs.execute();
    
  • 既存の結果セットから populate() メソッドを使用して入力する。

    rs.populate(resultSet);
    

    注意 :

    ResultSet.TYPE_FORWARD_ONLY の結果セットを使用している場合、以下の条件で RowSet に入力しようとすると SQLException が送出されます。
    • 結果セットのカーソルが行 1 を超える位置にある状態で CachedRowset.populate(ResultSet rs) を呼び出した場合。

    • newPosition の値が結果セットの現在のカーソル位置より小さい状態で CachedRowset.populate(ResultSet rs, int newPosition) を呼び出した場合。


CachedRowSet へのメタデータの設定

RowSet のデータ変更をデータベースとの間で同期するために、RowSet にメタデータを設定することが必要な場合もあります。詳細については、「RowSet に対するデータベース更新用メタデータの設定」を参照してください。

CachedRowSet のデータの操作

キャッシュされた RowSet に一連のデータ行を入力した後には、結果セットのデータを扱うのとほぼ同じ方法でそのキャッシュ済みのデータを取り扱えます。ただし、データベースに対して変更を行う前には、acceptChanges() を明示的に呼び出す必要があります。


注意 :

RowSet のカラム名やテーブル名に区切り識別子は使用できません。SQL 文の中では、区切り識別子は二重引用符で囲む必要があります。区切り識別子には、SQL 予約語 (USERDATE など) になっているものや、識別子ではない名前になっているものがあります。有効な識別子は必ず文字で始まり、文字、数字、およびアンダースコアのみを使用できます。

RowSet の行からデータを取得する

RowSet からデータを取得するには、結果セットの場合と同様に getXXX メソッドを使用します。次に例を示します。

while (rs.next ())
   {
   int id = rs.getInt (1);
   String fname = rs.getString ("FIRST_NAME");
   String mname = rs.getString ("MIDDLE_NAME");
   String lname = rs.getString ("LAST_NAME"));
   }

RowSet の行を更新する

通常、データの更新は以下の手順で行います。

  1. 行または挿入行に移動します。

  2. updateXXX メソッドで行を変更します。

  3. updateRow() または insertRow() で操作を完了します。

操作を完了しても変更はデータベースとの間で同期されないことに注意します。変更は RowSet に対してのみ行われます。acceptChanges() を呼び出して、明示的に変更を同期させる必要があります。詳細については、下記の「RowSet の変更をデータベースとの間で同期する」を参照してください。

RowSet を操作する際、WebLogic Server では内部的に各操作の後で RowSet にライフ サイクルのステージが設定され、その後は現在のライフ サイクルのステージに基づいて RowSet に対する以降の操作が制限されます。update メソッド群で行の変更を開始したら、必ず updateRow() または insertRow() で操作を完了します。完了してはじめて、別の行の操作 (別の行へのカーソルの移動を含む) ができるようになります。RowSet のライフ サイクルのステージと、各ステージで許可される操作の詳細については、「RowSet を使用したプログラミング」を参照してください。

行を更新するには、更新する行にカーソルを移動して、その行の個別のカラムに対して updateXXX メソッドを呼び出してから、updateRow() を呼び出して操作を完了します。次に例を示します。

rs.first();
rs.updateString(4, "Francis");
rs.updateRow();

注意 :

複数のテーブルの同名のカラムを更新する場合、カラムの参照には Update 文でカラムのインデックス番号を使用する必要があります。

RowSet に行を挿入する

行を挿入するには、新規の挿入行にカーソルを移動してその行のカラムの値を更新してから、insertRow() を呼び出して RowSet に行を追加します。次に例を示します。

rs.moveToInsertRow();
rs.updateInt(1, 104);
rs.updateString("FIRST_NAME", "Yuri");
rs.updateString("MIDDLE_NAME", "M");
rs.updateString("LAST_NAME", "Zhivago");
rs.updateString("PHONE", "1234567812");
rs.updateString("EMAIL", "Yuri@poet.com");
rs.insertRow();
rs.moveToCurrentRow();

行を挿入した後には、明示的にカーソルを移動する必要があります。カーソルが暗黙的に移動することはありません。

RowSet の行を削除する

RowSet の行を削除するには、カーソルをその行に移動して deleteRow() を呼び出します。次に例を示します。

rs.last();
rs.deleteRow();

RowSet の変更をデータベースとの間で同期する

RowSet の個別の行を変更した後には、acceptChanges() を呼び出して変更をデータベースに伝播します。次に例を示します。

rs.acceptChanges();

acceptChanges() を呼び出すと、RowSet では、すでに RowSet で使用されているデータベース接続情報を使用するか (「データベース接続のオプション」を参照)、acceptChanges(connection) メソッドで渡された接続オブジェクトを使用して、データベースに接続します。1 つまたは複数の行を変更した後に acceptChanges() を呼び出せます。RowSet に対してすべての変更を行った後に acceptChanges() を呼び出すと、RowSet からデータベースへの接続が 1 度だけになるのでより効率的です。

WebLogic Server で RowSet を使用する場合、データベースに対する書き込みと読み取りには weblogic.jdbc.rowset.WLSyncProvider オブジェクトが内部的に使用されます。WLSyncProvider ではデータベースの変更にオプティミスティックな同時実行方式が使用されます。つまり、RowSet への入力時から RowSet のデータの変更がデータベースに伝播されるまでの間に、データベースのデータが別のプロセスによって変更されないと仮定されます。データベースに変更を書き込む前には、WLSyncProvider によってデータベースのデータと RowSet の元の値 (RowSet の作成時または最後の同期の際に RowSet に読み込まれた値) が比較されます。データベースの値になんらかの変更があった場合には javax.sql.rowset.spi.SyncProviderException が送出され、データベースにはどの変更も書き込まれません。アプリケーションでこの例外を捕捉して、処理方法を決定できます。詳細については、「SyncResolver を使用した SyncProviderException の処理」を参照してください。

WLCachedRowSet インタフェースは CachedRowSet インタフェースの拡張で、このインタフェースでオプティミスティックな同時実行性ポリシーを選択できます。詳細については、「オプティミスティックな同時実行性ポリシー」を参照してください。

変更をデータベースに伝播した後には、アプリケーションの環境に応じて RowSet のライフ サイクルのステージが「Designing」(設計) または「Populating」(入力) に変わります。「Designing」(設計) ステージの場合、再び使用する前に RowSet に再入力する必要があります。「Populating」(入力) ステージの場合、現在のデータを持つ RowSet を使用できます。詳細については、「トランザクション完了後の WebLogic RowSet の再利用」を参照してください。

RowSet を再び使用する予定がない場合は、close() メソッドで閉じる必要があります。次に例を示します。

rs.close();

RowSet に対するデータベース更新用メタデータの設定

SQL クエリで RowSet に入力する場合、WebLogic の RowSet 実装では ResultSetMetaData インタフェースを使用して、自動的に RowSet 内のデータのテーブル名とカラム名が認識されます。ほとんどの場合、RowSet が変更をデータベースに書き戻すために必要な SQL を生成するには、この情報で十分です。ただし一部の JDBC ドライバでは、このクエリで返される行にテーブルおよびカラムのメタデータが含まれません。RowSet のデータ変更をデータベースとの間で同期しようとすると、次のエラーが表示されます。

java.sql.SQLException: Unable to determine the table name for column: 
column_name. Please ensure that you've called WLRowSetMetaData.setTableName to 
set a table name for this column.

テーブル名がない場合、RowSet は読み込み操作にのみ使用できます。テーブル名をプログラム的に指定しない限り、RowSet では更新を発行できません。また、setKeyColumns() メソッドによる主キー カラムの設定が必要な場合もあります。次に例を示します。

rs.setTableName(PHYSICIAN);
rs.setKeyColumns(new int[] { 1 });

詳細については、javax.sql.rowset.CachedRowSet インタフェースの説明を参照してください。

メタデータを扱うための WebLogic RowSet 拡張

以下の節では、RowSet に対する適切なメタデータの取得または設定に使用できる WebLogic RowSet 拡張について説明します。

executeAndGuessTableName と executeAndGuessTableNameAndPrimaryKeys

SQL クエリで RowSet に入力する場合、通常は execute() メソッドを使用してクエリを実行し、データを読み込みます。WLCachedRowSet 実装には、関連付けられたテーブルのメタデータも識別するために execute メソッドを拡張した executeAndGuessTableName メソッドおよび executeAndGuessTableNameAndPrimaryKeys メソッドが用意されています。

executeAndGuessTableName メソッドでは、関連付けられた SQL を解析して、すべてのカラムのテーブル名を、SQL キーワード FROM に続く最初の語として設定します。

executeAndGuessTableNameAndPrimaryKeys メソッドでは、SQL コマンドを解析してテーブル名を読み取ってから、java.sql.DatabaseMetaData を使用して、テーブルの主キーを識別します。


注意 :

これらのメソッドは DBMS または JDBC ドライバのサポートに依存しています。すべての DBMS、すべての JDBC ドライバで機能するとは限りません。

MetaData インタフェースを使用したテーブルおよび主キー情報の設定

WLRowSetMetaData インタフェースを使用してテーブルおよび主キー情報を手動で設定することもできます。

WLRowSetMetaData metaData = (WLRowSetMetaData) rowSet.getMetaData();
// すべてのカラムに 1 つのテーブル名を設定する
metaData.setTableName("employees");

または

metaData.setTableName("e_id", "employees");
metaData.setTableName("e_name", "employees");

WLRowSetMetaData インタフェースを使用して主キー カラムを指定することもできます。

metaData.setPrimaryKeyColumn("e_id", true);

詳細については、weblogic.jdbc.rowset.WLRowSetMetaData の Javadoc を参照してください。

書き込みテーブルの設定

WLRowSetMetaData インタフェースには、更新または削除するテーブルのみを指定するための setWriteTableName メソッドがあります。このメソッドは通常、複数のテーブルの結合が入力されている RowSet において、1 つのテーブルのみを更新する場合に使用します。書き込みテーブルに属さないカラムは読み込み専用としてマークされます。

たとえば、RowSet に「注文」と「顧客」の結合が含まれるとします。書き込みテーブルを「注文」に設定します。deleteRow を呼び出すと、「注文」の行は削除されますが、「顧客」の行は削除されません。


注意 :

JSR-114 は、WebLogic CachedRowSetMetaData.setWriteTableName メソッドと同じ機能を備えた CachedRowSet.setTableName (http://java.sun.com/javase/6/docs/api/javax/sql/rowset/CachedRowSet.html#setTableName(java.lang.String) を参照) を提供します。どちらのメソッドを呼び出しても、書き込みテーブルに属さないカラムが読み取り専用のものとしてマークされます。WebLogic ではまた、カラムがどのテーブルに属するかのマッピングに使用される CachedRowSetMetaData.setTableName メソッドが用意されています。setTableName を使用して書き込みテーブルを設定する際には、アプリケーションに適した API を使用してメソッドを実装するように注意してください。

RowSet とトランザクション

ほとんどのデータベースや JDBC アプリケーションではトランザクションを使用します。RowSet は JTA トランザクションを含むトランザクションをサポートしています。一般的な使用例は次のとおりです。トランザクション 1 で RowSet を取得します。トランザクション 1 がコミットされます。基底のデータに対して、データベースやアプリケーション サーバによるロックはありません。RowSet はメモリ内にデータを保持します。データを変更したり、ネットワーク経由でクライアントに提供したりできます。アプリケーションはデータベースに対する変更をコミットする場合、トランザクション 2 を開始して、RowSet の acceptChanges メソッドを呼び出します。その後トランザクション 2 がコミットされます。

JTA グローバル トランザクションとの統合

EJB コンテナと UserTransaction インタフェースは JTA トランザクション マネージャを使用してトランザクションを開始します。RowSet の操作はこのトランザクションに参加することができます。JTA トランザクションに参加するには、RowSet はトランザクション対応の DataSource (TxDataSource) を使用する必要があります。DataSource は WebLogic Server コンソールでコンフィグレーションできます。

acceptChanges でオプティミスティックな衝突や他の例外が発生した場合、RowSet はグローバル JTA トランザクションを中止します。アプリケーションは通常データを再び読み込んで、新しいトランザクションで更新を再び処理します。

グローバル トランザクションを使用する RowSet の動作

障害またはロールバックが発生すると、データはデータベースからロールバックされますが、RowSet からはロールバックされません。作業を進める前に、次のいずれかを行う必要があります。

  • rowset.refresh を呼び出して、データベースからのデータで RowSet を更新する。

  • 現在のデータで新しい RowSet を作成する。

ローカル トランザクションの使用

JTA グローバル トランザクションを使用しない場合、RowSet はローカル トランザクションを使用します。最初に接続に対する setAutoCommit(false) を呼び出します。次に SQL 文をすべて発行して、最後に connection.commit() を呼び出します。これでローカル トランザクションをコミットしようとします。EJB または JMS コンテナによって開始された JTA トランザクションと統合する場合は、このメソッドは使用しないでください。

acceptChanges でオプティミスティックな衝突や他の例外が発生した場合、RowSet はローカル トランザクションをロールバックします。この場合、acceptChanges で発行された SQL は、いずれもデータベースにコミットされません。

ローカル トランザクションを使用する RowSet の動作

この節では、失敗したローカル トランザクションにおける RowSet の動作について説明します。この動作は、接続オブジェクトの種類によって異なります。

connection.commit の呼び出し

この状況では、接続オブジェクトは RowSet によって作成されたものではなく、connection.commit を呼び出すことによってローカル トランザクションを開始します。トランザクションが失敗するか、または接続が connection.rollback を呼び出すと、データはデータベースからロールバックされますが、RowSet においてはロールバックされません。作業を進める前に、次のいずれかを行う必要があります。

  • rowset.refresh を呼び出して、データベースからのデータで RowSet を更新する。

  • 現在のデータで新しい RowSet を作成する。

acceptChanges の呼び出し

この状況では、RowSet が独自の接続オブジェクトを作成し、それを使用して acceptChanges を呼び出すことにより RowSet 内のデータを更新します。障害が発生した場合、または RowSet が connection.rollback を呼び出した場合、データは RowSet からもデータベースからもロールバックされます。

トランザクション完了後の WebLogic RowSet の再利用

多くの場合、RowSet の変更をデータベースとの間で同期した後に、その RowSet を現在入力されているデータのまま使用することが必要になるでしょう。そうすることでデータベースとの往復回数が減るため、アプリケーションのパフォーマンスを向上させることができます。ただし、RowSet とそのデータを再利用する場合、そのデータにさらに変更を加える前に、その RowSet が参加するトランザクションが必ずすべて完了している必要があります。

ローカル トランザクションで RowSet を使用していて、RowSet データの変更をデータベースとの間で同期する前に、接続オブジェクトに autocommit=true と設定されている場合、データの同期後に現在の値のまま RowSet を再利用できます。これは、autocommit の設定によってローカル トランザクションがすぐに強制的に完了するためです。RowSet の変更前には、ローカル トランザクションが必ず完了します。

以下のいずれかの状況で RowSet を使用している場合、すべてのトランザクションが必ず自動的に完了するとは限りません。

  • グローバル トランザクションで使用している

  • ローカル トランザクションで、autocommit=false と設定された接続オブジェクトを使用してデータの変更をデータベースと同期している

これらのいずれかの場合、現在のデータで RowSet を再利用する前に、acceptChanges() を呼び出してデータベースとの間で変更を同期した後で、tx.commit() または java.sql.Connection.commit() の代わりに javax.sql.rowset.CachedRowSet.commit() を呼び出してトランザクションをコミットします。CachedRowSet.commit() メソッドには Connection.commit() メソッドがラップされています。これを使用すると必ずトランザクションが完了され、その後に RowSet を変更できるようになります。

FilteredRowSet

以下の節では、WebLogic Server における標準の FilteredRowSet の使い方について説明します。

FilteredRowSet の特性

FilteredRowSet を使用するとキャッシュされた行のサブセットを取り扱え、データベースに接続されていない状態で行のサブセットを変更できます。フィルタ処理された RowSet とは、分かりやすく言えば、キャッシュされている RowSet の中で、特定の行のみを閲覧、移動、操作できるものをいいます。FilteredRowSet には以下のような特性があります。

  • 利用できる行は、アプリケーションで指定した javax.sql.rowset.Predicate オブジェクトで決定され、setFilter() メソッドで指定される。

  • Predicate オブジェクトには javax.sql.rowset.Predicate インタフェースを実装する必要がある。Predicate インタフェースには public boolean evaluate(RowSet rs) メソッドがあり、このメソッドで RowSet の各行を評価します。

    • このメソッドから true が返された場合、その行は利用可能で閲覧することもできる。

    • このメソッドから false が返された場合、その行は利用も閲覧もできない。

    詳細については「FilteredRowSet に対するフィルタの設定」を参照してください。

  • WebLogic Server には weblogic.jdbc.rowset.SQLPredicate クラスという javax.sql.rowset.Predicate インタフェースの実装が用意されている。このクラスを使用すると、SQL のような WHERE 句の構文を使用して FilteredRowSet のフィルタを定義できます。「SQLPredicate、SQL 方式の RowSet フィルタ」を参照してください。

特有のプログラミング上の考慮事項

RowSet フィルタは累積的ではない

FilteredRowSet の WebLogic 実装では現在、FilteredRowSet にフィルタを 2 回設定すると古いフィルタが新しいフィルタで置き換えられます。JSR-114 にはこの点についてはっきりとした規定がありません。Oracle では上記のような動作を setFilter メソッドを実装する適切な方式と判断しました。Sun の参照実装は、これと同じようには動作しません。Sun の実装では RowSet のフィルタされた行がさらにフィルタされます。この場合 2 回目のフィルタを変更して必要なすべての条件をフィルタするようにすると、同じ効果を得られます。

フィルタの設定、変更前に保留中の変更を解決する

RowSet フィルタを設定または変更する前に RowSet に保留中の変更がある場合、その変更を受け入れる (acceptChanges() を呼び出す) か、RowSet のデータを変更前の状態に戻す (restoreOriginal() を呼び出す) 必要があります。WebLogic Server では行われる可能性のある変更を示せるように RowSet 内の移動が認められており、ユーザは RowSet フィルタを変更する前に上記のどちらかのメソッドを呼び出す必要があります。acceptChanges() ではデータベースへのアクセスが行われ、restoreOriginal() では行われません。

FilteredRowSet のサンプル コード

以下のサンプルに、キャッシュされた RowSet を作成してから WebLogic Server SQLPredicate を使用してフィルタを適用および変更する方法について示します。

コード リスト 6-2 FilteredRowSet のサンプル コード

import javax.sql.rowset.FilteredRowSet;
import weblogic.jdbc.rowset.RowSetFactory;
import weblogic.jdbc.rowset.SQLPredicate;
public class FilteredRowSetDemo {
public static void main (String[] args) {
// 「DESIGNING」(設計) ライフサイクル ステージ - RowSet を作成してプロパティを設定する
  try {
    // RowSetFactory インスタンスを作成して、このファクトリから
    // WLCachedRowSet を作成し FilteredRowSet にキャストする
    RowSetFactory rsfact = RowSetFactory.newInstance();
    FilteredRowSet rs = rsfact.newCachedRowSet();
    // DataSource を介してデータベース アクセスを設定する
    // 他のオプションについては、「データベース接続のオプション」を参照
    rs.setDataSourceName(examples-dataSource-demoPool);
    rs.setCommand("SELECT ID, FIRST_NAME, MIDDLE_NAME, LAST_NAME,
    PHONE, EMAIL FROM PHYSICIAN WHERE ID>?");
   // 「CONFIGURE QUERY」(クエリのコンフィグレーション) ライフサイクル操作 - クエリ パラメータに値を設定する
    rs.setInt(1, 0); 
   // 「POPULATING」(入力) ライフサイクル ステージ - RowSet に入力するためのコマンドを実行する
    rs.execute();
  }
// 「CONFIGURING METADATA」(メタデータのコンフィグレーション) - 初めに入力してから、KeyColumn を含むメタデータを設定する
  rs.setKeyColumns(new int[] { 1 });
  while (rs.next ())
// 「移動」操作により RowSet が「MANIPULATING」(操作) ライフサイクル ステージになる
    {
    System.out.println ("ID: " +rs.getInt (1));
    System.out.println ("FIRST_NAME: " +rs.getString (2));
    System.out.println ("MIDDLE_NAME: " +rs.getString (3));
    System.out.println ("LAST_NAME: " +rs.getString (4));
    System.out.println ("PHONE: " +rs.getString (5));
    System.out.println ("EMAIL: " +rs.getString (6));
    }
  }
// RowSet を「DESIGNING」(設計) または「POPULATING」(入力) ステージにするために
// 変更を受け入れるか restoreOriginal を呼び出す必要がある
// 移動後に RowSet は「MANIPULATING」(操作) ステージになり
// このライフサイクル ステージではプロパティは変更できない
  rs.restoreOriginal();
// フ ィ ル タ を 設 定
// SQLPredicate クラスを使用して SQLPredicate オブジェクトを作成してから
// そのオブジェクトを setFilter メソッドに渡して RowSet をフィルタ処理する
  SQLPredicate filter = new SQLPredicate("ID >= 103");
  rs.setFilter(filter);
  System.out.println("Filtered data: ");
  while (rs.next ())
    {
    System.out.println ("ID: " +rs.getInt (1));
    System.out.println ("FIRST_NAME: " +rs.getString (2));
    System.out.println ("MIDDLE_NAME: " +rs.getString (3));
    System.out.println ("LAST_NAME: " +rs.getString (4));
    System.out.println ("PHONE: " +rs.getString (5));
    System.out.println ("EMAIL: " +rs.getString (6));
    System.out.println (" ");
    }
// RowSet を「DESIGNING」(設計) または「POPULATING」(入力) ライフサイクル ステージにするために
// 変更を受け入れるか restoreOriginal を呼び出す必要がある
// 移動後に RowSet は「MANIPULATING」(操作) ステージになり
// このライフサイクル ステージではプロパティは変更できない
  rs.restoreOriginal();
// フ ィ ル タ を 変 更 す る
  SQLPredicate filter2 = new SQLPredicate("ID <= 103");
  rs.setFilter(filter2);
  System.out.println("Filtered data: ");
  while (rs.next ())
    {
    System.out.println ("ID: " +rs.getInt (1));
    System.out.println ("FIRST_NAME: " +rs.getString (2));
    System.out.println ("MIDDLE_NAME: " +rs.getString (3));
    System.out.println ("LAST_NAME: " +rs.getString (4));
    System.out.println ("PHONE: " +rs.getString (5));
    System.out.println ("EMAIL: " +rs.getString (6));
    System.out.println (" ");
    }
// RowSet を「DESIGNING」(設計) または「POPULATING」(入力) ライフサイクル ステージにするために
// 変更を受け入れるか restoreOriginal を呼び出す必要がある
// 移動後に RowSet は「MANIPULATING」(操作) ステージになり
// このライフサイクル ステージではプロパティは変更できない
  rs.restoreOriginal();
// フ ィ ル タ を 削 除 す る
  rs.setFilter(null);
  while (rs.next ())
    {
    System.out.println ("ID: " +rs.getInt (1));
    System.out.println ("FIRST_NAME: " +rs.getString (2));
    System.out.println ("MIDDLE_NAME: " +rs.getString (3));
    System.out.println ("LAST_NAME: " +rs.getString (4));
    System.out.println ("PHONE: " +rs.getString (5));
    System.out.println ("EMAIL: " +rs.getString (6));
    System.out.println (" ");
    }
  rs.close();
  }
}

FilteredRowSet のクラスとインタフェースのインポート

標準の FilteredRowSet を使用するには、次のクラスをインポートする必要があります。

javax.sql.rowset.FilteredRowSet;
weblogic.jdbc.rowset.RowSetFactory;

上記のサンプル コードでは、フィルタの作成に weblogic.jdbc.rowset.SQLPredicate クラスも使用されています。アプリケーションでは、weblogic.jdbc.rowset.SQLPredicate クラスを使用することも、独自のフィルタ クラスを作成することも可能です。詳細については「FilteredRowSet に対するフィルタの設定」を参照してください。

FilteredRowSet の作成

RowSet はファクトリ インタフェースから作成されます。WebLogic Server で FilteredRowSet を作成するには、主に以下の手順に従います。

  1. RowSetFactory インスタンスを作成します。このインスタンスはアプリケーションで使用する RowSet オブジェクトを作成するためのファクトリとして機能します。次に例を示します。

    RowSetFactory rsfact = RowSetFactory.newInstance();
    
  2. WLCachedRowSet を作成して javax.sql.rowset.FilteredRowSet オブジェクト型にキャストします。デフォルトでは WebLogic newCachedRowSet() RowSetFactory メソッドで WLCachedRowSet オブジェクトが作成されます。このオブジェクトをそのまま使用することもできますが、標準の FilteredRowSet オブジェクトを使用する場合は、WLCachedRowSet オブジェクトを FilteredRowSet オブジェクトにキャストできます。次に例を示します。

    FilteredRowSet rs = rsfact.newCachedRowSet();
    

FilteredRowSet のプロパティの設定

FilteredRowSet のプロパティのオプションは CachedRowSet のオプションと同じです。「CachedRowSet のプロパティの設定」を参照してください。

FilteredRowSet のデータベース接続のオプション

FilteredRowSet のデータベース接続のオプションは CachedRowSet のオプションと同じです。「データベース接続のオプション」を参照してください。

FilteredRowSet への入力

FilteredRowSet のデータ入力のオプションは CachedRowSet のオプションと同じです。「CachedRowSet への入力」を参照してください。

FilteredRowSet のメタデータの設定

RowSet のデータ変更をデータベースとの間で同期するために、RowSet にメタデータを設定することが必要な場合もあります。詳細については、「RowSet に対するデータベース更新用メタデータの設定」を参照してください。

FilteredRowSet に対するフィルタの設定

FilteredRowSet の行をフィルタするには、setFilter メソッドを呼び出して、Predicate (フィルタ) オブジェクトをこのメソッドのパラメータとして渡します。Predicate オブジェクトは javax.sql.rowset.Predicate インタフェースを実装するクラスのインスタンスです。FilteredRowSet の WebLogic 実装では、独自のフィルタを定義することも、weblogic.jdbc.rowset.SQLPredicate クラスのインスタンスを使用することもできます。

ユーザ定義の RowSet フィルタ

FilteredRowSet 用のフィルタを定義する場合、主に以下の手順に従います。

  1. 使用するフィルタ動作 (表示する行を特定のカラムの値を持つ行に制限するなど) を行う javax.sql.rowset.Predicate インタフェースを実装するクラスを定義します。たとえば、ID カラムの値の範囲に基づいて表示する行を制限する場合、ID カラムの値をフィルタするロジックを含むクラスを定義します。

  2. クラスのインスタンス (フィルタ) を作成して、使用するフィルタ条件を指定します。たとえば、ID カラムの値が 100 から 199 までの行のみを表示するといった指定が可能です。

  3. rowset.setFilter() を呼び出して、クラスをこのメソッドのパラメータとして渡します。

コード リスト 6-3 に、javax.sql.rowset.Predicate インタフェースを実装するクラスのサンプルを示します。このサンプルでは、カラムの値の大文字と小文字を区別しないバージョンを評価するフィルタを作成することができるクラスが示されます。コード リスト 6-4 に、フィルタ条件を判断するクラスのインスタンスを作成してから、FilteredRowSet 用のフィルタとしてフィルタ オブジェクトを設定するコードを示します。

コード リスト 6-3 javax.sql.rowset.Predicate を実装するフィルタ クラス

package examples.jdbc.rowsets;
import javax.sql.rowset.Predicate;
import javax.sql.rowset.CachedRowSet;
import javax.sql.RowSet;
import java.sql.SQLException;
public class SearchPredicate implements Predicate, java.io.Serializable {
  private boolean DEBUG = false;
  private String col = null;
  private String criteria = null;
  // カラムと値の大文字と小文字を区別しない比較を作成するコンストラクタ
  public SearchPredicate(String col, String criteria) {
    this.col = col;
    this.criteria = criteria;
  }
  public boolean evaluate(RowSet rs)  {
    CachedRowSet crs = (CachedRowSet)rs;
    boolean bool = false;
    try {
      debug("evaluate(): "+crs.getString(col).toUpperCase()+" contains "+
          criteria.toUpperCase()+" = "+
          crs.getString(col).toUpperCase().contains(criteria.toUpperCase()));
      if (crs.getString(col).toUpperCase().contains(criteria.toUpperCase()))
        bool = true;
    } catch(Throwable t) {
      t.printStackTrace();
      throw new RuntimeException(t.getMessage());
    }
    return bool;
  }
  public boolean evaluate(Object o, String s) throws SQLException {
    throw new SQLException("String evaluation is not supported.");
  }
  public boolean evaluate(Object o, int i) throws SQLException {
    throw new SQLException("Int evaluation is not supported.");
  }
}

コード リスト 6-4 FilteredRowSet 用のフィルタを設定するコード

SearchPredicate pred = new SearchPredicate(ROWSET_LASTNAME, lastName);
rs.setFilter(pred);

WebLogic SQL 方式フィルタ

Weblogic Server には javax.sql.rowset.Predicate インタフェースを実装する weblogic.jdbc.rowset.SQLPredicate クラスが用意されています。SQLPredicate クラスを使用すると、SQL のような WHERE 句の構文を使用するフィルタを定義して RowSet の行をフィルタできます。次に例を示します。

SQLPredicate filter = new SQLPredicate("ID >= 103");
rs.setFilter(filter);

詳細については、「SQLPredicate、SQL 方式の RowSet フィルタ」を参照してください。

FilteredRowSet のデータの操作

FilteredRowSet のデータの操作は CachedRowSet のデータの操作とほぼ同様です。ただし、行を挿入したり更新したりする場合には、必ずフィルタ条件の範囲内で変更を行って、操作した行が表示される行の集合に残るようにします。たとえば、RowSet のフィルタで ID カラムの値が 105 より小さい行のみを表示できる場合、ID カラムの値が 106 の行を挿入しようとしたり、ID の値を 106 に更新しようとしたりすると、操作は失敗して SQLException が送出されます。

データ操作の詳細については、「CachedRowSet のデータの操作」を参照してください。

WebRowSet

WebRowSet は、RowSet を XML フォーマットで読み書きできるキャッシュされた RowSet です。WebRowSet には以下のような特性があります。

詳細については、Sun の Web サイト (http://java.sun.com/products/jdbc/download.html) および javax.sql.rowset.WebRowSet インタフェースの Javadoc (http://java.sun.com/javase/6/docs/api/javax/sql/rowset/WebRowSet.html) を参照してください。


注意 :

WebLogic Server では RowSet について 2 つのスキーマがサポートされています。1 つは標準 WebRowSet 用、もう 1 つは WLCachedRowSet 用です。これは、JSR-114 の最終仕様より前に実装されたものです。

特有のプログラミング上の考慮事項

  • WebLogic の WebRowSet 実装では 2 つの XML スキーマ (および API) がサポートされる。1 つは標準の WebRowSet 仕様で (http://java.sun.com/xml/ns/jdbc/webrowset.xsd で入手可能)、もう 1 つは WLCachedRowSet 用です。これは、JSR-114 の最終仕様より前に実装されたものです。

  • WebLogic Server の RowSet のみを使用している場合、どちらのスキーマを使用することもできる。専用のスキーマには、以下のような利点があります。

    • より多くの種類の要素がある。

    • Oracle Workshop for WebLogic Platform の RowSet で使用される。

  • 他の RowSet の実装と対話するには、標準のスキーマを使用する必要がある。

JoinRowSet

JoinRowSet とは、SQL JOIN によって単一の RowSet に結合された、多数の接続されていない RowSet オブジェクトです。JoinRowSet には以下のような特性があります。

表 6-1 一致するカラムで使用できるデータ型

結合の左側のデータ型 結合の右側で使用できるデータ型

Number

Number

String

Boolean

Boolean

String

Date

Date

String

String

String

Number

Boolean

Date


JoinRowSet の詳細については、javax.sql.rowset.Joinable (http://java.sun.com/javase/6/docs/api/javax/sql/rowset/Joinable.html) および JoinRowSet インタフェース (http://java.sun.com/javase/6/docs/api/javax/sql/rowset/JoinRowSet.html) の Javadoc を参照してください。

JDBCRowSet

JDBCRowSet は ResultSet オブジェクトのラッパーで、これによって結果セットを JavaBean コンポーネントとして使用できるようになります。JDBCRowSet は、接続されている RowSet です。これ以外の種類の RowSet はすべて接続されていない RowSet です。

詳細については、javax.sql.rowset.JdbcRowSet インタフェースの Javadoc (http://java.sun.com/javase/6/docs/api/javax/sql/rowset/JdbcRowSet.html) を参照してください。

SyncResolver を使用した SyncProviderException の処理

acceptChanges() を呼び出して RowSet の変更をデータベースに伝播する場合、WebLogic Server では RowSet の元のデータ (最後の同期以降のデータ) が、データベースのデータとオプティミスティックな同時実行性ポリシーに基づいて比較されます。データの変更が検出されると、javax.sql.rowset.spi.SyncProviderException が送出されます。デフォルトでは、アプリケーションでは何も行う必要はありませんが、RowSet の変更はデータベースと同期されません。こうした例外を処理してシステムに適した方法でデータ変更を行うように、アプリケーションを設計できます。


注意 :

javax.sql.rowset.CachedRowSets については、RowSet のすべての行にあるすべての元の値が、対応するデータベースの行と比較されます。weblogic.jdbc.rowset.WLCachedRowSet、または他の WebLogic 拡張の RowSet では、オプティミスティックな同時実行性の設定に基づいてデータが比較されます。詳細については、「オプティミスティックな同時実行性ポリシー」を参照してください。

SyncProviderException を処理する主な手順は次のとおりです。

  1. javax.sql.rowset.spi.SyncProviderException を捕捉します。

  2. この例外から SyncResolver オブジェクトを取得します。「SyncResolver オブジェクトの取得」を参照してください。

  3. nextConflict() または任意の他の移動用メソッドを使用して、衝突まで移動します。「SyncResolver オブジェクト内の移動」を参照してください。

  4. 適切な値を判断して、setResolvedValue() (RowSet に値を設定するメソッド) で設定します。「RowSet データの同期の衝突に対する解決後の値の設定」を参照してください。

  5. 衝突している値ごとに、手順 3 と 4 を繰り返します。

  6. RowSet に対して (SyncResolver ではなく) rowset.acceptChanges() を呼び出し、新しい解決後の値を使用してデータベースとの間で変更を同期します。「変更の同期」を参照してください。

SyncResolver および SyncProviderException の詳細については、RowSet の仕様または SyncResolver インタフェースの Javadoc を参照してください。


注意 :

SyncProviderException の解決を開始する前に、他のどのプロセスもデータを更新することのないようにします。

RowSet データの同期における衝突の種類

表 6-2 に、RowSet からデータベースにデータの変更を同期する際に起こる、衝突状況の種類について示します。

表 6-2 RowSet の変更をデータベースに同期する際の衝突の種類

RowSet データの変更の種類 データベース データの変更の種類 メモ

更新

更新

RowSet とデータベースの同じ行の値が変更された。SyncResolver のステータスは SyncResolver.UPDATE_ROW_CONFLICT。

場合によりアプリケーションで、この衝突を解決するロジックを提供するか、ユーザに新しいデータを提示することが必要になる。

更新

削除

RowSet の行の値は更新されたが、データベースではその行が削除された。SyncResolver のステータスは SyncResolver.UPDATE_ROW_CONFLICT。

場合によりアプリケーションで、行を削除されたままにしておく (データベースの状態のようにする) か、行を元に戻して RowSet からの変更を永続化するかを決定するロジックの提供が必要になる。

  • 行を削除されたままにするには、RowSet の行の変更を元に戻す。

  • 行を元に戻して変更するには、目的の値を持つ新しい行を挿入する。

データベースで行が削除されている場合、衝突している値がないことに注意する。getConflictValue() を呼び出すと、weblogic.jdbc.rowset.RowNotFoundException が送出される。

削除

更新

RowSet の行は削除されたが、データベースではその行が更新された。SyncResolver のステータスは SyncResolver.DELETE_ROW_CONFLICT。

場合によりアプリケーションで、行を削除する (RowSet の状態のようにする) か、行を保持してデータベースでの現在の変更を永続化するかを決定するロジックの提供が必要になる。

この場合、行のすべての値が衝突した値になる。行を保持してデータベースの現在の値とするには、setResolvedValue を呼び出して、行の各カラムに解決後の値を設定してデータベースの現在の値にする。削除するには、syncprovider.deleteRow() を呼び出す。

削除

削除

RowSet で行が削除され、データベースでは別のプロセスでその行が削除された。Syncresolver のステータスは SyncResolver.DELETE_ROW_CONFLICT。

SyncProviderException を解決するには、RowSet のその行に対する削除処理を取り消す必要がある。

行のどのカラムにも、衝突している値がない (null もない) ことに注意する。getConflictValue() を呼び出すと、weblogic.jdbc.rowset.RowNotFoundException が送出される。

挿入

挿入

RowSet に行が挿入され、データベースにも別の行が挿入された場合、主キーの衝突が起こることがある。その場合 SQL 例外が送出される。SyncProviderException は送出されないので、この種類の衝突を SyncResolver を使って直接的に処理することはできない。


SyncResolver のサンプル コード

コード リスト 6-5 に、RowSet とデータベースの間で衝突している値を SyncResolver を使って解決する方法についての要約されたサンプルを示します。このサンプルでは、衝突のある SyncResolver の各行について、認識されている名前を持つカラムの値をチェックします。このサンプルの詳細については、サンプルの後の節で説明します。

コード リスト 6-5 SyncResolver の要約済みサンプル コード

try {
    rs.acceptChanges();
} catch (SyncProviderException spex) {
    SyncResolver syncresolver = spex.getSyncResolver();
    while (syncresolver.nextConflict())  {
        int status = syncresolver.getStatus();
        int rownum = syncresolver.getRow();
        rs.absolute(rownum);
        // 各カラムの null をチェックする
        // 衝突を書き出す
        // 解決後の値をこのサンプル用のデータベースの値に設定する
        // データベースで削除された行についての例外を処理する
        try {
           Object idConflictValue = syncresolver.getConflictValue("ID");
           if (idConflictValue != null)  {
              System.out.println("ID value in db: " + idConflictValue);
              System.out.println("ID value in rowset: " + rs.getInt("ID"));
              syncresolver.setResolvedValue("ID", idConflictValue);
              System.out.println("Set resolved value to " + idConflictValue);
            }
           else {
              System.out.println("ID: NULL - no conflict");
           }
        } catch (RowNotFoundException e)  { 
           System.out.println("An exception was thrown when requesting a ");
          System.out.println("value for ID. This row was ");
          System.out.println("deleted in the database.");
        }
. . .
    }
    try {
        rs.acceptChanges();
    } catch (Exception ignore2) {
        }
}

SyncResolver オブジェクトの取得

SyncProviderException を処理するために、例外を捕捉してそこから SyncResolver オブジェクトを取得できます。次に例を示します。

try {
   rowset.acceptChanges();
} catch (SyncProviderException spex) {
   SyncResolver syncresolver = spex.getSyncResolver();
. . .
}

SyncResolver は、SyncResolver インタフェースを実装する RowSet です。SyncResolver オブジェクトには、元の RowSet のすべての行に対応する行が含まれます。衝突していない値については、SyncResolver での値は null になります。衝突している値については、データベースの現在の値が値として格納されます。

SyncResolver オブジェクト内の移動

SyncResolver オブジェクトを使用すると、すべての衝突に移動して、衝突している各値に適切な値を設定できます。SyncResolver インタフェースには nextConflict() メソッドと previousConflict() メソッドが含まれており、これらを使用して null 以外の衝突している値を持つ SyncResolver 内で、直接次の行に移動できます。SyncResolver オブジェクトは RowSet なので、SyncResolver の任意の行へのカーソル移動には、すべての RowSet 移動用メソッドも使用できます。ただし、nextConflict() メソッドおよび previousConflict() メソッドを使用すると、衝突している値のない行を簡単にスキップできます。

衝突している行にカーソルを移動した後には、各カラムの値を getConflictValue() メソッドでチェックして、RowSet 内の値と衝突しているデータベース内の値を検索してから、それらの値を比較して衝突の処理方法を決定する必要があります。値が衝突していない行の場合、戻り値として null が返されます。行がデータベースでは削除されていた場合、戻り値はなく、例外が送出されます。


注意 :

WebLogic の RowSet 実装では、データベースの行の任意の値と、RowSet の作成時または最後の同期時に RowSet に読み込まれた値が異なる場合、値の衝突が発生します。

RowSet とデータベースの値を比較するコードのサンプルを以下に示します。

syncresolver.nextConflict()
for (int i = 1; i <= colCount; i++) {
   if (syncresolver.getConflictValue(i) != null)  {
      rsValue = rs.getObject(i);
      resolverValue = syncresolver.getConflictValue(i);
      . . . 
     // RowSet および SyncResolver の値を比較して
     // どちらを解決後の値 (永続化する値) とするかを決定する
   } 
} 

RowSet データの同期の衝突に対する解決後の値の設定

データベースに永続化するための適切な値を設定するには、setResolvedValue() を呼び出します。次に例を示します。

syncresolver.setResolvedValue(i, resolvedValue);

setResolvedValue() メソッドでは以下の変更が行われます。

  • データベースに永続化するための値を設定する。つまり、RowSet の現在の値を設定します。変更が同期されると、この新しい値がデータベースに永続化されます。

  • RowSet のデータの元の値をデータベースの現在の値に変更する。元の値とは、最後の同期以降に設定されていた値です。setResolvedValue() を呼び出すと、元の値がデータベースの現在の値になります。

  • 同期の呼び出しにおける WHERE 句を、データベースの適切な行が更新されるように変更する。

変更の同期

SyncResolver で衝突している値を解決した後には、変更をデータベースとの間で同期する必要があります。これを行うには、rowset.acceptChanges() をもう一度呼び出します。acceptChanges() 呼び出しでは SyncResolver オブジェクトがクローズされ、同期の完了後にデータベースのロックが解放されます。

WLCachedRowSet

WLCachedRowSet は CachedRowSet、FilteredRowSet、WebRowSet、および SortedRowSet の拡張です。JoinRowSet には以下のような特性があります。

詳細については、weblogic.jdbc.rowset.WLCachedRowSet インタフェースの Javadoc を参照してください。

SharedRowSet

RowSet は 1 つのスレッドでのみ使用できます。複数のスレッドで共有することはできません。SharedRowSet は CachedRowSet を拡張したもので、元の CachedRowSet のデータに基づいて、他のスレッドで使用する CachedRowSet を追加作成できます。SharedRowSet には以下のような特性があります。

SharedRowSet を作成するには、WLCachedRowSet インタフェースの createShared() メソッドを使用して、その結果を WLCachedRowSet 型にキャストします。次に例を示します。

WLCachedRowSet sharedrowset = (WLCachedRowSet)rowset.createShared();

SortedRowSet

SortedRowSet は CachedRowSet を拡張して、CachedRowSet の行をアプリケーションで提供される Comparator オブジェクトに基づいてソートできるようにしたものです。SortedRowSet には以下のような特性があります。

詳細については、Javadocs で以下について参照してください。

SQLPredicate、SQL 方式の RowSet フィルタ

ここでは、SQLPredicate クラスについて説明します。

SQLPredicate とは

Weblogic Server には javax.sql.rowset.Predicate インタフェースの実装である weblogic.jdbc.rowset.SQLPredicate クラスが用意されています。SQLPredicate クラスを使用すると、SQL のような WHERE 句の構文を使用して FilteredRowSet のフィルタを定義できます。

SQLPredicate の文法

SQLPredicate クラスの文法は JMS セレクタの文法から借用されたもので、SQL SELECT 文の WHERE 句の文法とよく似ています。

重要な注意事項を以下に示します。

  • カラムを参照する場合、カラム名を使用する必要がある。カラムのインデックス番号は使用できません。

  • この文法では、演算子および数学演算がサポートされる。たとえば次のようなことができます。

     (colA + ColB) >=100.
    
  • WHERE 句の作成では、単純なデータ型のみを使用できる。以下のようなデータ型を使用できます。

    • String

    • Int

    • Boolean

    • Float

  • 以下のような複合データ型はサポートされない。

    • Array

    • BLOB

    • CLOB

    • Date

サンプル コード

// フ ィ ル タ を 設 定
// SQLPredicate クラスを使用して SQLPredicate オブジェクトを作成してから
// そのオブジェクトを setFilter メソッドに渡して RowSet をフィルタ処理する
SQLPredicate filter = new SQLPredicate("ID >= 103");
rs.setFilter(filter);

詳細については、weblogic.jdbc.rowset.SQLPredicate クラスの Javadoc を参照してください。

オプティミスティックな同時実行性ポリシー

ほとんどの場合、RowSet へのデータの入力とデータベースの更新は別々のトランザクションで発生します。データベース内の基底のデータは 2 つのトランザクションの間に変更される可能性があります。WebLogic Server の RowSet 実装 (WLCachedRowSet) では、オプティミスティックな同時実行性の制御を使用して、データベースの一貫性を確保します。

オプティミスティックな同時実行性の場合、RowSet は複数のユーザが同じデータを同時に変更する可能性は低いという前提で動作します。そのため、接続されていない RowSet モデルの一端として、RowSet ではデータベース リソースがロックされません。ただし、データベースに変更を書き込む前に、データベース内の変更予定のデータが、そのデータが RowSet に読み込まれたときから変更されていないことを RowSet でチェックする必要があります。

RowSet から発行される UPDATE 文と DELETE 文には、データベース内のデータを、RowSet の入力時に読み込まれたデータに照らして検証するための WHERE 句が含まれます。RowSet ではデータベース内の基底のデータが変更されたことが検出されると OptimisticConflictException が発行されます。アプリケーションではこの例外を捕捉して処理方法を決定できます。一般的には、アプリケーションで、変更されたデータを更新してユーザに再度提示します。

WLCachedRowSet 実装には複数のオプティミスティックな同時実行性ポリシーが用意されています。これらのポリシーによって、基底のデータベースのデータを検証するために RowSet から発行される SQL が決まります。

これらのポリシーの違いを例示するために、以下のような内容の例を使用します。

以下に示すオプティミスティックな同時実行性ポリシーのそれぞれの例では、RowSet に従業員テーブルからこの行が読み込まれ、John Smith の給与が 20000 に設定されます。続いて、オプティミスティックな同時実行性ポリシーが RowSet から発行される SQL コードにどのように影響するかが示されます。

VERIFY_READ_COLUMNS

デフォルトでは、RowSet のオプティミスティックな同時実行性制御ポリシーは VERIFY_READ_COLUMNS です。RowSet から UPDATE または DELETE が発行されるときに、データベースから読み込まれたすべてのカラムが WHERE 句に含まれます。これによって、RowSet に最初に読み込まれたすべてのカラムの値について変更されてないかどうかが検証されます。

この例では、RowSet から次のように発行されます。

UPDATE employees SET e_salary = 20000 
   WHERE e_id = 1 AND e_salary=10000 AND e_name = 'John Smith';

VERIFY_MODIFIED_COLUMNS

VERIFY_MODIFIED_COLUMNS ポリシーでは、主キー カラムと更新されるカラムのみが WHERE 句に含まれます。アプリケーションで更新されるカラムの一貫性だけを考慮する場合に便利です。データを読み込んだ後に更新の対象外のカラムが変更されていても、この更新をコミットできます。

この例では、RowSet から次のように発行されます。

UPDATE employees SET e_salary = 20000 
   WHERE e_id = 1 AND e_salary=10000 

e_id カラムは主キー カラムなので含まれています。e_salary カラムは更新されるカラムなので、同様に含まれています。e_name カラムは読み込まれただけなので、検証されません。

VERIFY_SELECTED_COLUMNS

VERIFY_SELECTED_COLUMNS では、主キー カラムと指定するカラムが WHERE 句に含まれます。

WLRowSetMetaData metaData = (WLRowSetMetaData) rowSet.getMetaData();
metaData.setOptimisticPolicy(WLRowSetMetaData.VERIFY_SELECTED_COLUMNS);
// e_salary カラムのみを検証する
metaData.setVerifySelectedColumn("e_salary", true);

metaData.acceptChanges();

この例では、RowSet から次のように発行されます。

UPDATE employees SET e_salary = 20000 
   WHERE e_id = 1 AND e_salary=10000 

e_id カラムは主キー カラムなので含まれています。e_salary カラムは選択されたカラムなので、同様に含まれています。

VERIFY_NONE

VERIFY_NONE ポリシーでは、主キー カラムのみが WHERE 句に含まれます。データベース データに対して追加で検証は行われません。

この例では、RowSet から次のように発行されます。

UPDATE employees SET e_salary = 20000 WHERE e_id = 1

VERIFY_AUTO_VERSION_COLUMNS

VERIFY_AUTO_VERSION_COLUMNS では、主キー カラムと、別個のバージョン カラムが WHERE 句に含まれます。RowSet では更新の一環としてバージョン カラムの自動的なインクリメントも行われます。このバージョン カラムのデータ型は integer でなければなりません。データベース スキーマを更新して、別個のバージョン カラム (e_version) を含める必要があります。例では、このカラムの現在の値が 1 であると仮定します。

metaData.setOptimisticPolicy(WLRowSetMetaData.
   VERIFY_AUTO_VERSION_COLUMNS);

metaData.setAutoVersionColumn("e_version", true);

metaData.acceptChanges();

この例では、RowSet から次のように発行されます。

UPDATE employees SET e_salary = 20000, e_version = 2
WHERE e_id = 1 AND e_version = 1

e_version カラムは SET 句で自動的にインクリメントされます。WHERE 句は主キー カラムとバージョン カラムを検証しています。

VERIFY_VERSION_COLUMNS

VERIFY_VERSION_COLUMNS の場合、RowSet では主キー カラムと、別個のバージョン カラムがチェックされます。RowSet では更新の一環としてバージョン カラムはインクリメントされません。データベース スキーマを更新して、別個のバージョン カラム (e_version) を含める必要があります。例では、このカラムの現在の値が 1 であると仮定します。

metaData.setOptimisticPolicy(WLRowSetMetaData.VERIFY_VERSION_COLUMNS);
metaData.setVersionColumn("e_version", true);
metaData.acceptChanges();

この例では、RowSet から次のように発行されます。

UPDATE employees SET e_salary = 20000
WHERE e_id = 1 AND e_version = 1

WHERE 句では主キー カラムとバージョン カラムが検証されます。RowSet でバージョン カラムがインクリメントされないため、この処理はデータベースで行う必要があります。一部のデータベースでは、行の更新時に自動的にインクリメントするバージョン カラムを提供しています。データベース トリガを使用してこのタイプの更新を処理することもできます。

オプティミスティックな同時実行性制御の制限

オプティミスティックなポリシーでは、変更する行に対する UPDATE 文と DELETE 文のみが検証されます。読み込み専用の行はデータベースに照らして検証されません。

ほとんどのデータベースでは WHERE 句での BLOB または CLOB カラムの使用が許可されていないため、RowSet では BLOB または CLOB カラムの検証はできません。

RowSet に複数のテーブルが含まれる場合、RowSet では更新されたテーブルのみが検証されます。

オプティミスティックなポリシーの選択

デフォルトの VERIFY_READ_COLUMNS では、パフォーマンスを多少犠牲にして、強力なレベルの一貫性が提供されます。最初に読み込まれたすべてのカラムをデータベースに送信したり、データベースと比較したりする必要があるため、このポリシーでは多少のオーバーヘッドが追加で発生します。VERIFY_READ_COLUMNS は強力なレベルの一貫性が必要な場合に適しています。バージョン カラムを含めるようにデータベース テーブルを変更することはできません。

VERIFY_SELECTED_COLUMNS は、開発者が検証を完全に制御する必要があり、アプリケーションに固有の知識を使用して SQL を微調整する場合に便利です。

VERIFY_AUTO_VERSION_COLUMNS では、VERIFY_READ_COLUMNS と同じレベルの一貫性が提供されますが、integer 型の 1 つのカラムを比較するだけで済みます。このポリシーではバージョン カラムのインクリメントも行われるため、データベースの設定が若干必要です。

VERIFY_VERSION_COLUMNS は、最高レベルのパフォーマンスと一貫性が必要なプロダクション システムにお勧めします。VERIFY_AUTO_VERSION_COLUMNS と同様に、高いレベルの一貫性が提供されますが、データベースで 1 つのカラムの比較が行われるだけです。VERIFY_VERSION_COLUMNS では、データベースでバージョン カラムのインクリメントを行う必要があります。一部のデータベースでは、更新時に自動的にインクリメントするカラム タイプを提供していますが、この動作はデータベース トリガを使用して実装することもできます。

VERIFY_MODIFIED_COLUMNS と VERIFY_NONE では一貫性が確保される程度は低下しますが、オプティミスティックな衝突が起きる可能性も少なくなります。高レベルなデータの一貫性の必要性よりも、パフォーマンス、衝突の回避の方が重要な場合は、これらのポリシーを検討します。

パフォーマンスのオプション

RowSet を使用する場合は以下のパフォーマンス オプションを検討します。

JDBC のバッチ処理

RowSet 実装では JDBC のバッチ処理もサポートされています。各 SQL 文を JDBC ドライバに個々に送信する代わりに、バッチ処理では文の集合を 1 つの一括処理として JDBC ドライバに送信します。バッチ処理はデフォルトで無効になっていますが、一般に 1 つのトランザクションで多数の更新が発生する場合にパフォーマンスが向上します。アプリケーションとデータベースに対してこのオプションを有効および無効に設定して、ベンチマークを実行してみるとよいでしょう。

WLCachedRowSet インタフェースには、INSERT、DELETE、および UPDATE 文のバッチ処理を制御するための setBatchInserts(boolean)setBatchDeletes(boolean)、および setBatchUpdates(boolean) メソッドがあります。


注意 :

setBatchInsertssetBatchDeletessetBatchUpdates メソッドは、acceptChanges メソッドを呼び出す前に呼び出す必要があります。

Oracle Database のバッチ処理の制限

WLCachedRowSet はオプティミスティックな同時実行性制御に依存しているため、更新または削除コマンドが成功したか、オプティミスティックな衝突が発生したかを判別する必要があります。WLCachedRowSet 実装では、文によって更新された行の数についての JDBC ドライバの報告に基づいて、衝突が発生したかどうかを判別します。更新された行が 0 の場合、WLCachedRowSet では衝突が発生したと認識されます。

Oracle JDBC ドライバではバッチ更新が実行されると java.sql.Statement.SUCCESS_NO_INFO が返されるため、RowSet 実装では衝突が発生したかどうかを判別するために戻り値を使用できません。

バッチ処理が Oracle データベースで使用されることが RowSet で検出された場合、バッチ処理の動作は自動的に変更されます。

バッチ挿入は検証されないため、通常どおり実行されます。

バッチ更新は正常に実行されますが、RowSet では特別な SELECT クエリが発行され、バッチ更新でオプティミスティックな衝突が発生したかどうかがチェックされます。

SELECT 検証クエリの後でバッチ削除を実行するよりも効率的なため、バッチ削除ではグループ削除が使用されます。

グループ削除

複数の行を削除する場合、RowSet では通常、削除する行ごとに DELETE 文が発行されます。グループ削除を有効にした場合、RowSet では削除する行を含む WHERE 句が指定された DELETE 文が 1 つだけ発行されます。

たとえば、テーブルから 3 人の従業員を削除する場合、通常であれば RowSet から次のように発行されます。

DELETE FROM employees WHERE e_id = 3 AND e_version = 1;
DELETE FROM employees WHERE e_id = 4 AND e_version = 3;
DELETE FROM employees WHERE e_id = 5 AND e_version = 10;

グループ削除を有効にした場合、RowSet から次のように発行されます。

DELETE FROM employees 
WHERE e_id = 3 AND e_version = 1 OR
      e_id = 4 AND e_version = 3 OR
      e_id = 5 AND e_version = 10;

WLRowSetMetaData.setGroupDeleteSize を使用すると、1 つの DELETE 文に含まれる行の数を決定できます。デフォルト値は 50 です。