3秒でわかる
JDBCで自動コミットの有無を切り替えるメソッド。複数のSQLをひとつのトランザクションとしてまとめたいときに false にします。
もう少し詳しく
どういうものか
setAutoCommit は、Java の JDBC で Connection が持つメソッドです。既定値は true で、この状態では SQL を1文実行するたびに自動でコミットが走り、その時点でデータベースへ確定します。setAutoCommit(false) を呼ぶと自動確定が止まり、commit() を明示的に呼ぶまで変更が確定しなくなります。途中で rollback() を呼べば、それまでの変更をまとめて取り消せます。
なぜ必要か
現実の処理は、複数の更新が全部そろってはじめて意味を持つことがあります。口座 A から引いて口座 B に足す振替で、引いた直後に例外が出た場合、自動コミットのままだと A から消えた金額がどこにも存在しない状態で確定します。片方だけ成立した中途半端な状態を残さないために、複数文をひとつの単位として扱う必要があります。
性能上の理由もあります。1万件の INSERT を自動コミットのまま流すと、1件ごとにログの書き込みと同期が発生します。まとめてコミットする形に変えるだけで、処理時間が桁で変わることは珍しくありません。
具体例
Connection conn = dataSource.getConnection();
try {
conn.setAutoCommit(false);
try (PreparedStatement out = conn.prepareStatement(
"UPDATE accounts SET balance = balance - ? WHERE id = ?")) {
out.setInt(1, 5000);
out.setString(2, "A001");
out.executeUpdate();
}
try (PreparedStatement in = conn.prepareStatement(
"UPDATE accounts SET balance = balance + ? WHERE id = ?")) {
in.setInt(1, 5000);
in.setString(2, "B002");
in.executeUpdate();
}
conn.commit(); // ここではじめて両方が確定する
} catch (SQLException e) {
conn.rollback(); // 片方だけ成立した状態を残さない
throw e;
} finally {
conn.setAutoCommit(true);
conn.close();
}つまずきやすいところ
いちばん多いのは commit() の呼び忘れです。false にしたまま接続を閉じると、多くのドライバは暗黙にロールバックするため、例外も出ないのに更新が消えます。エラーログが何も出ないので、原因にたどり着くまで時間がかかります。
次に、コネクションプールを使っている場合の後始末です。プールから借りた接続は close しても実際には破棄されず、プールへ返却されるだけです。false のまま返すと、次にその接続を借りた別の処理が自動コミットの効かない状態で走ります。finally で true に戻すのが定石です。
もうひとつ、MySQL の MyISAM のようにトランザクションを持たないストレージエンジンでは、false にしてもロールバックは効きません。テーブルが InnoDB かどうかを先に確認します。
似た用語との違い
| 語 | 位置づけ |
|---|---|
| setAutoCommit(false) | トランザクションの開始にあたる操作 |
| commit | それまでの変更を確定する |
| rollback | それまでの変更を取り消す |
| @Transactional | Spring が上記の呼び出しを肩代わりする注釈 |