JDBCのRowSetを使う

 RowSetは、JDBCで伝統的に使われているResultSetインターフェースを拡張したものです。 JavaBeans的なプロパティを持ち、 結果集合の他に、データベース接続情報やSQL文も内部に持つことができます。 つまり(Statement+ResultSet)のようなものです。

 RowSetは最も基本的なインターフェースで、 それを発展させたサブインターフェースが性質によって幾つか提供されていますが、 最も特徴的な性質を持つのはCachedRowSetというインターフェースです。 これは、ResultSetや他のRowSetのサブインターフェースと異なり、 「DB接続を保持しておく必要がない」という特徴を持ちます。 ResultSetに格納した結果集合は、DB接続を終了すると失われてしまいます。 そのため、たくさんのクライアントからDBアクセスがある一般のWebアプリケーションでは、 一旦ResultSetから別のオブジェクト(通常2次元の配列やリスト)にコピーする手間が発生します。 RowSetはDB接続を終了してもデータがそのまま参照でき、 実際にJSF(Java Server Faces)では、 RowSetに格納したデータをJavaコーディング不要でHTMLのTABLE形式で出力するカスタムタグを提供しています。

 これらRowSetの実装クラスは、新しいJ2SE 5.0(Tiger)には標準で同梱されていますが、 まだ広く使われているJ2SE 1.4.xでも、別途RowSetの標準実装を入手すれば、使うことができます。 それはhttp://java.sun.com/products/jdbc/ のRowSet Reference Implementationから無償でダウンロードできます。 現在のバージョンは1.0.1です。また、 こちら の1.0 Early Accessも使えます。 ダウンロードしたアーカイブを展開すると、rowset.jarというファイルがあるので、 これをCLASSPATHに追加します。

package test.jdbc.rowset;

import java.sql.Connection;
import javax.sql.rowset.CachedRowSet;
import com.sun.rowset.CachedRowSetImpl;

public class CachedRowSetExample1 {

    public static void main(String[] args) throws Exception {
        Class.forName("com.mysql.jdbc.Driver");
        CachedRowSet crset = new CachedRowSetImpl();

        crset.setCommand("SELECT * FROM SHITEN ORDER BY SHITEN_CODE");
        crset.setUrl("jdbc:mysql://localhost/udb" +
                "?useUnicode=true&characterEncoding=SJIS");
        crset.setUsername("urano");
        crset.setPassword("momomo");
        crset.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
        crset.execute();
        while(crset.next()){
            System.out.println(crset.getString(1) + "\t" +
                    crset.getString(2) + "\t" + crset.getInt(5));
        }
    }
}

 1つのテーブルの全件を取得し、順番に表示するだけの簡単なサンプルです。 DB接続はCachedRowSetが内部的に行います。 が、従来のJDBCのAPI(Connectionを作り、Statementを作り、問合せを発行する) を使った後、CachedRowSet#populate()というメソッドで、 取得したResultSetオブジェクトからデータをRowSetオブジェクトに転写することも可能です。

package test.jdbc.rowset;

import java.sql.Connection;
import javax.sql.rowset.CachedRowSet;
import com.sun.rowset.CachedRowSetImpl;

public class CachedRowSetExample2 {

    public static void main(String[] args) throws Exception {
        Class.forName("com.mysql.jdbc.Driver");
        CachedRowSet crset = new CachedRowSetImpl();

        crset.setCommand("SELECT * FROM SHITEN ORDER BY SHITEN_CODE");
        crset.setUrl("jdbc:mysql://localhost/udb" +
                "?useUnicode=true&characterEncoding=SJIS");
        crset.setUsername("urano");
        crset.setPassword("momomo");
        crset.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
        crset.execute();
        while(crset.next()){
            String shitenName = crset.getString(2);
            if(shitenName.equals("上野")){
                crset.updateInt(5, 450);
                crset.updateRow();
                System.out.println("更新しました。");
            }
        }
        crset.acceptChanges();
        System.out.println("処理完了");
    }
}

 今度はRowSet上のデータを更新することで、DBにもその変更を反映させるサンプルです。 更新対象の行はこの例のようにnext()で一行ずつ探すこともできますが、 absolute()メソッドで直接対象行に移動することでも可能です。 それでupdate???()メソッドで各カラムの値を更新し、 updateRow()で行の更新を確定し、 最後にacceptChanges()でDBに更新を反映(&コミット)します。

RIのバグ?
 データベースがMySQLで、 現在のSunのRIで、CacheRowSetImplにプレースホルダー(?)を使うと、 次のようなエラーになり、使えないようです。

java.sql.SQLException: Can not issue executeUpdate() for SELECTs
  at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1746)
  at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1597)
  at com.sun.rowset.internal.CachedRowSetReader.readData(CachedRowSetReader.java:146)
  at com.sun.rowset.CachedRowSetImpl.execute(CachedRowSetImpl.java:583)
  at com.sun.rowset.CachedRowSetImpl.execute(CachedRowSetImpl.java:1165)

参考URL:http://lists.mysql.com/java/6496

JDBC and Swing Top

(first uploaded 2004/11/11 last updated 2004/12/22, URANO398)

PC用眼鏡【管理人も使ってますがマジで疲れません】 解約手数料0円【あしたでんき】 Yahoo 楽天 NTT-X Store

無料ホームページ 無料のクレジットカード 海外格安航空券 ふるさと納税 海外旅行保険が無料! 海外ホテル