ファイヤープロジェクト
トランザクション
2004-05-05T18:00+09:00   matsu
JDBCのデフォルト動作はautocommitである.Connectionオブジェクトのメソッド呼び出しにより,autocommitのon/offやCOMMIT,ROLLBACKができる.また,きれいな方法ではないがJDBCによるDBアクセスプログラムは,基本的にプログラムでSQL文を生成して発行するだけなので,BEGINとかCOMMITというSQL文を発行すればトランザクション処理ができる.
JDBCではデフォルトではautocommitとなっている.すなわち1SQL文で1トランザクションとなり自動でCOMMITがかかる.これはautocommitのon/offはConnectionオブジェクトで設定する.すなわち接続単位に設定することになる.さらにConnectionオブジェクトのcommit,rollbackメソッドによりCOMMITとROLLBACKを行う.
以下にautocommitをoffとしてトランザクション処理を行うサンプルを示す.
このサンプルでは以下を行う.
  1. DB接続
  2. テーブル作成.COMMIT.
  3. 作成したテーブルのレコード数を問い合わせる
  4. 1トランザクションブロック内でインサート.COMMIT.
  5. 作成したテーブルのレコード数を問い合わせる
  6. テーブル削除.COMMIT.
  7. DB切断
これを実行してみる.
$> java  TransactionSample jdbc:postgresql://dbserver/firstdb matsu hogefuga 
Connect to jdbc:postgresql://dbserver/firstdb
User : matsu Pwd : hogefuga
Execute SQL : CREATE TABLE jdbcsample (id INTEGER PRIMARY KEY, value TEXT);
Execute SQL : SELECT count(*) AS record_count from jdbcsample ;
Got record : count = 0
Execute SQL : INSERT INTO jdbcsample VALUES(0,'Value0'); ... Inserted 1 record.
Execute SQL : INSERT INTO jdbcsample VALUES(1,'Value1'); ... Inserted 1 record.
Execute SQL : INSERT INTO jdbcsample VALUES(2,'Value2'); ... Inserted 1 record.
Execute SQL : INSERT INTO jdbcsample VALUES(3,'Value3'); ... Inserted 1 record.
Execute SQL : INSERT INTO jdbcsample VALUES(4,'Value4'); ... Inserted 1 record.
Execute SQL : SELECT count(*) AS record_count from jdbcsample ;
Got record : count = 5
Execute SQL : DROP TABLE jdbcsample;
上の出力はdoTransactionメソッド内でcommitしている場合のものである.二回目のSELECT(5回のINSERT後)の結果count = 5となっている.
dbConnection.commit ();
//      dbConnection.rollback();
ここで上のcommit呼び出しをコメントアウトし,rollback呼び出しを有効にしてみる.
$> java  TransactionSample jdbc:postgresql://dbserver/firstdb matsu hogefuga 
Connect to jdbc:postgresql://dbserver/firstdb
User : matsu Pwd : hogefuga
Execute SQL : CREATE TABLE jdbcsample (id INTEGER PRIMARY KEY, value TEXT);
Execute SQL : SELECT count(*) AS record_count from jdbcsample ;
Got record : count = 0
Execute SQL : INSERT INTO jdbcsample VALUES(0,'Value0'); ... Inserted 1 record.
Execute SQL : INSERT INTO jdbcsample VALUES(1,'Value1'); ... Inserted 1 record.
Execute SQL : INSERT INTO jdbcsample VALUES(2,'Value2'); ... Inserted 1 record.
Execute SQL : INSERT INTO jdbcsample VALUES(3,'Value3'); ... Inserted 1 record.
Execute SQL : INSERT INTO jdbcsample VALUES(4,'Value4'); ... Inserted 1 record.
Execute SQL : SELECT count(*) AS record_count from jdbcsample ;
Got record : count = 0
Execute SQL : DROP TABLE jdbcsample;
doTransactionメソッド内でcommitの代わりにrollbackしているので,二回目のSELECT文によるcountの値が0である.
サンプルではautocommitのon/offの設定は以下で行っている.
// 接続
Connection dbConnection = DriverManager.getConnection (url, user, pwd);
// autocommitの解除
dbConnection.setAutoCommit (false);
これでautocommitが無効になる.あとは必要に応じてConnectionオブジェクトに対しCOMMIT,ROLLBACKを指示する.以下はサンプルのdoTransaction内の処理.
try {
  for (int i = 0; i < 5; i++)
    {
      doInsert (dbConnection, i, "Value" + i);
    }
  dbConnection.commit ();
  //      dbConnection.rollback();
} catch (SQLException sqle) {
    sqle.printStackTrace ();
    System.err.println ("ROLLBACK START");
    dbConnection.rollback ();
    System.err.println ("ROLLBACK DONE");
}
tryブロックでINSERTを5回してCOMMITするようにして,catchブロックでROLLBACKするようにしている.commit,rollbackメソッドもSQLExepctionを投げるので,doTransactionメソッドをthrows SQLExceptionとしている.
Connectionオブジェクトのメソッド呼び出しではなく,JDBCの上位プログラムでBEGIN,COMMITなどのSQL文を発行することができた.その方法を以下に示すが推奨されない方法である.さて,JDBCのデフォルトではautocommitなので,トランザクションブロックはSQL文
BEGIN;
で開始し
COMMIT;
あるいは
ROLLBACK;
で終了する必要がある.いずれのSQL文もレコードが選択されることはないので,executeUpdateによって発行する.JDBCを利用してトランザクション処理を行うサンプルを以下に示す.
このサンプルでは以下を行う.
  1. DB接続
  2. テーブル作成
  3. 作成したテーブルのレコード数を問い合わせる
  4. 1トランザクションブロック内でインサート
  5. 作成したテーブルのレコード数を問い合わせる
  6. テーブル削除
  7. DB切断
以下は実行結果.
$ java TransactionSample jdbc:postgresql://dbserver/firstdb matsu hogefuga
Connect to jdbc:postgresql://dbserver/firstdb
User : matsu Pwd : hogefuga
Execute SQL : CREATE TABLE jdbcsample (id INTEGER PRIMARY KEY, value TEXT);
Execute SQL : SELECT count(*) AS record_count from jdbcsample ;
Got record : count = 0
Execute SQL : BEGIN;
Execute SQL : INSERT INTO jdbcsample VALUES(0,'Value0'); ... Inserted 1 record.
Execute SQL : INSERT INTO jdbcsample VALUES(1,'Value1'); ... Inserted 1 record.
Execute SQL : INSERT INTO jdbcsample VALUES(2,'Value2'); ... Inserted 1 record.
Execute SQL : INSERT INTO jdbcsample VALUES(3,'Value3'); ... Inserted 1 record.
Execute SQL : INSERT INTO jdbcsample VALUES(4,'Value4'); ... Inserted 1 record.
Execute SQL : COMMIT;
Execute SQL : SELECT count(*) AS record_count from jdbcsample ;
Got record : count = 5
Execute SQL : DROP TABLE jdbcsample;
INSERT後COMMITするので,その後のSELECTの結果count = 5となっている.確認までに,サンプル中のcommitTransactionメソッド呼び出しをrollbackTransactionメソッド呼び出しに置き換えて実行してみた.
$ java TransactionSample jdbc:postgresql://dbserver/firstdb matsu hogefuga
Connect to jdbc:postgresql://dbserver/firstdb
User : matsu Pwd : hogefuga
Execute SQL : CREATE TABLE jdbcsample (id INTEGER PRIMARY KEY, value TEXT);
Execute SQL : SELECT count(*) AS record_count from jdbcsample ;
Got record : count = 0
Execute SQL : BEGIN;
Execute SQL : INSERT INTO jdbcsample VALUES(0,'Value0'); ... Inserted 1 record.
Execute SQL : INSERT INTO jdbcsample VALUES(1,'Value1'); ... Inserted 1 record.
Execute SQL : INSERT INTO jdbcsample VALUES(2,'Value2'); ... Inserted 1 record.
Execute SQL : INSERT INTO jdbcsample VALUES(3,'Value3'); ... Inserted 1 record.
Execute SQL : INSERT INTO jdbcsample VALUES(4,'Value4'); ... Inserted 1 record.
Execute SQL : ROLLBACK;
Execute SQL : SELECT count(*) AS record_count from jdbcsample ;
Got record : count = 0
Execute SQL : DROP TABLE jdbcsample;
INSERT後ROLLBACKするので,その後のSELECTでのcountの値は0である.
サンプルではトランザクション処理はdoTransacationメソッドで行っている.すなわちdoTransaction内で明示的にトランザクションの開始と終了(BEGINとCOMMIT)を指定するためにbeginTransactionやcommitTransactionメソッドを呼び出している.
beginTransaction(dbConnection);
for (int i = 0; i < 5; i++)
  {
    doInsert (dbConnection, i, "Value" + i);
  }
commitTransaction(dbConnection);
beginTransactionメソッドにおいて,SQL文BEGINやCOMMITを発行している.
String sql = "BEGIN;";
Statement statement = dbConnection.createStatement ();
statement.executeUpdate (sql);
commitTransactionメソッドにおいてSQL文COMMITを発行している.
String sql = "COMMIT;";
Statement statement = dbConnection.createStatement ();
statement.executeUpdate (sql);
このようにBEGINやCOMMIT,そしてROLLBACKといったSQL文はexecuteUpdateで発行する.また,doTransactionメソッドの全体的な構造は以下のようになっている.
try{
  BEGIN;
  INSERT;
  COMMIT;
} catch (SQLException sqle) {
  ROLLBACK;
}
すなわちtryでトランザクション処理を行い,それに対するcatchとしてROLLBACKを実行する.また,ROLLBACK自体もSQLExceptionを投げ得るので,doTransactionメソッドはSQLExceptionを投げ得るという指定にした(※).
※ catch内でtry-catchとかしたくない.
トランザクションから話題が離れるが,本頁の二つのサンプルでは以下のようなSELECT文を発行した.
SELECT count(*) AS record_count from jdbcsample ;
このSQL文では集約関数countの列にあえてrecord_countという名前をつけている.そして以下のようにその名前を使用して値を取得している.
System.out.println ("Got record : count = " + resultSet.getInt ("record_count"));
このようにターゲットリストのフィールドにASで名前をつけると,あとでその名前を指定して値を取り出せる(※).
※ 他にもやりようはあるような気がするが,確認までにやってみた.
matsu(C)
Since 2002
Mail to matsu