JDBCのデフォルト動作はautocommitである.Connectionオブジェクトのメソッド呼び出しにより,autocommitのon/offやCOMMIT,ROLLBACKができる.また,きれいな方法ではないがJDBCによるDBアクセスプログラムは,基本的にプログラムでSQL文を生成して発行するだけなので,BEGINとかCOMMITというSQL文を発行すればトランザクション処理ができる.
概要
サンプル
Connectionオブジェクトによるトランザクション操作
BEGIN,COMMIT等のSQL文を発行する方法
トランザクションブロック
SELECTにおけるターゲットリスト
概要
JDBCではデフォルトではautocommitとなっている.すなわち1SQL文で1トランザクションとなり自動でCOMMITがかかる.これはautocommitのon/offはConnectionオブジェクトで設定する.すなわち接続単位に設定することになる.さらにConnectionオブジェクトのcommit,rollbackメソッドによりCOMMITとROLLBACKを行う.
サンプル
以下にautocommitをoffとしてトランザクション処理を行うサンプルを示す.
import java.sql.*;
public class TransactionSample
{
// PostgreSQL JDBCドライバのロード
private static final String PG_JDBC_DRIVER = "org.postgresql.Driver";
static
{
try
{
Class.forName (PG_JDBC_DRIVER);
}
catch (ClassNotFoundException e)
{
System.err.println ("JDBCドライバ " + PG_JDBC_DRIVER +
" がみつかりません.");
System.exit (-1);
}
}
public static void main (String args[])
{
String url;
String user;
String pwd;
if (args.length != 3)
{
System.err.println ("Usage : java JDBCConnectSample url user pwd");
System.exit (-1);
}
url = args[0];
user = args[1];
pwd = args[2];
System.out.println ("Connect to " + url);
System.out.println ("User : " + user + " Pwd : " + pwd);
try
{
// 接続
Connection dbConnection = DriverManager.getConnection (url, user, pwd);
// autocommitの解除
dbConnection.setAutoCommit (false);
// 各種クエリの発行
doCreate (dbConnection);
doSelect (dbConnection);
doTransaction (dbConnection);
doSelect (dbConnection);
doDrop (dbConnection);
// 切断
dbConnection.close ();
}
catch (SQLException sqle)
{
sqle.printStackTrace ();
System.exit (-1);
}
System.exit (0);
}
//テーブル作成
private static void doCreate (Connection dbConnection) throws SQLException
{
String sql =
"CREATE TABLE jdbcsample (id INTEGER PRIMARY KEY, value TEXT);";
System.out.println ("Execute SQL : " + sql);
try
{
Statement statement = dbConnection.createStatement ();
statement.executeUpdate (sql);
statement.close ();
dbConnection.commit ();
}
catch (SQLException sqle)
{
sqle.printStackTrace ();
dbConnection.rollback ();
}
}
// トランザクション処理
private static void doTransaction (Connection dbConnection)
throws SQLException
{
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");
}
}
//レコード投入
private static void doInsert (Connection dbConnection, int paramId,
String paramValue)
{
String sql =
"INSERT INTO jdbcsample VALUES(" + paramId + ",'" + paramValue + "');";
System.out.print ("Execute SQL : " + sql + " ... ");
try
{
Statement statement = dbConnection.createStatement ();
int insertNum = statement.executeUpdate (sql);
System.out.println ("Inserted " + insertNum + " record.");
statement.close ();
}
catch (SQLException sqle)
{
System.out.println ("");
sqle.printStackTrace ();
}
}
//検索
private static void doSelect (Connection dbConnection)
{
String sql = "SELECT count(*) AS record_count from jdbcsample ;";
System.out.println ("Execute SQL : " + sql);
try
{
Statement statement = dbConnection.createStatement ();
ResultSet resultSet = statement.executeQuery (sql);
while (resultSet.next ())
{
System.out.println ("Got record : count = " +
resultSet.getInt ("record_count"));
}
}
catch (SQLException sqle)
{
sqle.printStackTrace ();
}
}
//テーブル削除
private static void doDrop (Connection dbConnection) throws SQLException
{
String sql = "DROP TABLE jdbcsample;";
System.out.println ("Execute SQL : " + sql);
try
{
Statement statement = dbConnection.createStatement ();
statement.executeUpdate (sql);
statement.close ();
dbConnection.commit ();
}
catch (SQLException sqle)
{
sqle.printStackTrace ();
dbConnection.rollback ();
}
}
}
このサンプルでは以下を行う.
DB接続
テーブル作成.COMMIT.
作成したテーブルのレコード数を問い合わせる
1トランザクションブロック内でインサート.COMMIT.
作成したテーブルのレコード数を問い合わせる
テーブル削除.COMMIT.
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である.
Connectionオブジェクトによるトランザクション操作
サンプルでは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としている.
BEGIN,COMMIT等のSQL文を発行する方法
Connectionオブジェクトのメソッド呼び出しではなく,JDBCの上位プログラムでBEGIN,COMMITなどのSQL文を発行することができた.その方法を以下に示すが推奨されない方法である.さて,JDBCのデフォルトではautocommitなので,トランザクションブロックはSQL文
BEGIN;
で開始し
COMMIT;
あるいは
ROLLBACK;
で終了する必要がある.いずれのSQL文もレコードが選択されることはないので,executeUpdateによって発行する.JDBCを利用してトランザクション処理を行うサンプルを以下に示す.
import java.sql.*;
public class TransactionSampleRawSQL
{
// PostgreSQL JDBCドライバのロード
private static final String PG_JDBC_DRIVER = "org.postgresql.Driver";
static
{
try
{
Class.forName (PG_JDBC_DRIVER);
}
catch (ClassNotFoundException e)
{
System.err.println ("JDBCドライバ " + PG_JDBC_DRIVER +
" がみつかりません.");
System.exit (-1);
}
}
public static void main (String args[])
{
String url;
String user;
String pwd;
if (args.length != 3)
{
System.err.println ("Usage : java JDBCConnectSample url user pwd");
System.exit (-1);
}
url = args[0];
user = args[1];
pwd = args[2];
System.out.println ("Connect to " + url);
System.out.println ("User : " + user + " Pwd : " + pwd);
try
{
// 接続
Connection dbConnection = DriverManager.getConnection (url, user, pwd);
// 各種クエリの発行
doCreate (dbConnection);
doSelect (dbConnection);
doTransaction (dbConnection);
doSelect (dbConnection);
doDrop (dbConnection);
// 切断
dbConnection.close ();
}
catch (SQLException sqle)
{
sqle.printStackTrace ();
System.exit (-1);
}
System.exit (0);
}
//テーブル作成
private static void doCreate (Connection dbConnection)
{
String sql =
"CREATE TABLE jdbcsample (id INTEGER PRIMARY KEY, value TEXT);";
System.out.println ("Execute SQL : " + sql);
try
{
Statement statement = dbConnection.createStatement ();
statement.executeUpdate (sql);
statement.close ();
}
catch (SQLException sqle)
{
sqle.printStackTrace ();
}
}
// トランザクション処理
private static void doTransaction (Connection dbConnection)
throws SQLException
{
try
{
beginTransaction (dbConnection);
for (int i = 0; i < 5; i++)
{
doInsert (dbConnection, i, "Value" + i);
}
commitTransaction (dbConnection);
// rollbackTransaction(dbConnection);
}
catch (SQLException sqle)
{
sqle.printStackTrace ();
System.err.println ("ROLLBACK START");
rollbackTransaction (dbConnection);
System.err.println ("ROLLBACK DONE");
}
}
// BEGIN
private static void beginTransaction (Connection dbConnection)
throws SQLException
{
String sql = "BEGIN;";
System.out.println ("Execute SQL : " + sql);
Statement statement = dbConnection.createStatement ();
statement.executeUpdate (sql);
statement.close ();
}
// COMMIT
private static void commitTransaction (Connection dbConnection)
throws SQLException
{
String sql = "COMMIT;";
System.out.println ("Execute SQL : " + sql);
Statement statement = dbConnection.createStatement ();
statement.executeUpdate (sql);
statement.close ();
}
// ROLLBACK
private static void rollbackTransaction (Connection dbConnection)
throws SQLException
{
String sql = "ROLLBACK;";
System.out.println ("Execute SQL : " + sql);
Statement statement = dbConnection.createStatement ();
statement.executeUpdate (sql);
statement.close ();
}
//レコード投入
private static void doInsert (Connection dbConnection, int paramId,
String paramValue)
{
String sql =
"INSERT INTO jdbcsample VALUES(" + paramId + ",'" + paramValue + "');";
System.out.print ("Execute SQL : " + sql + " ... ");
try
{
Statement statement = dbConnection.createStatement ();
int insertNum = statement.executeUpdate (sql);
System.out.println ("Inserted " + insertNum + " record.");
statement.close ();
}
catch (SQLException sqle)
{
System.out.println ("");
sqle.printStackTrace ();
}
}
//検索
private static void doSelect (Connection dbConnection)
{
String sql = "SELECT count(*) AS record_count from jdbcsample ;";
System.out.println ("Execute SQL : " + sql);
try
{
Statement statement = dbConnection.createStatement ();
ResultSet resultSet = statement.executeQuery (sql);
while (resultSet.next ())
{
System.out.println ("Got record : count = " +
resultSet.getInt ("record_count"));
}
}
catch (SQLException sqle)
{
sqle.printStackTrace ();
}
}
//テーブル削除
private static void doDrop (Connection dbConnection)
{
String sql = "DROP TABLE jdbcsample;";
System.out.println ("Execute SQL : " + sql);
try
{
Statement statement = dbConnection.createStatement ();
statement.executeUpdate (sql);
statement.close ();
}
catch (SQLException sqle)
{
sqle.printStackTrace ();
}
}
}
このサンプルでは以下を行う.
DB接続
テーブル作成
作成したテーブルのレコード数を問い合わせる
1トランザクションブロック内でインサート
作成したテーブルのレコード数を問い合わせる
テーブル削除
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文を発行した.
SELECT count(*) AS record_count from jdbcsample ;
このSQL文では集約関数countの列にあえてrecord_countという名前をつけている.そして以下のようにその名前を使用して値を取得している.
System.out.println (“Got record : count = ” + resultSet.getInt (“record_count”));
このようにターゲットリストのフィールドにASで名前をつけると,あとでその名前を指定して値を取り出せる(※).
※ 他にもやりようはあるような気がするが,確認までにやってみた.