📌

Javaのtry-with-resourcesを利用してrollbackする

2022/08/07に公開

どこかでtry-with-resourcesを利用した際にrollbackしようとするとコネクションがクローズされていて出来ないみたいなものを見たので、これで出来るよという記事です。

rollback出来ないコード

問題のコードです。

final String connectionUrl = "jdbc:postgresql://localhost:5432/postgres";
final String user = "postgres";
final String password = "password";

final String sql1 = "insert into books values (2, 'Effective Java');";
final String sql2 = "insert into books values (3, 'Java言語で学ぶデザインパターン入門');";

Connection connection = DriverManager.getConnection(connectionUrl, user, password);

boolean isThrowException = true;
try (connection;
  PreparedStatement statement1 = connection.prepareStatement(sql1);
  PreparedStatement statement2 = connection.prepareStatement(sql2);
) {
  connection.setAutoCommit(false);
  statement1.executeUpdate();
  if (isThrowException) {
    throw new SQLException();
  }
  statement2.executeUpdate();
  connection.commit();
} catch (Exception e) {
  // ここに到達したときにはconnectionはcloseされている
  if (connection != null && !connection.isClosed()) {
    connection.rollback();
  }
  e.printStackTrace();
}

connection, statement1, statement2を自動クローズ対象のリソースとして指定しています。
この書き方の場合、コメントにも書いてあるようにcatchの中に入ったときにはすでにクローズされているためロールバック出来ません。

これはJavaの仕様でcatch, finallyに入った際にはすでにクローズされているためです。
JLS -> Extended try-with-resources

try-with-resourcesを利用してロールバックするコード

connectionで最初に囲ってネストした中にstatementを囲ってあげれば実現出来ます。

final String connectionUrl = "jdbc:postgresql://localhost:5432/postgres";
final String user = "postgres";
final String password = "password";

final String sql1 = "insert into books values (2, 'Effective');";
final String sql2 = "insert into books values (3, 'デザインパターン');";

boolean isThrowException = true;

try (Connection connection = DriverManager.getConnection(connectionUrl, user, password)) {
  connection.setAutoCommit(false);
  try (PreparedStatement statement1 = connection.prepareStatement(sql1);
       PreparedStatement statement2 = connection.prepareStatement(sql2)
  ) {
    statement1.executeUpdate();
    if (isThrowException) {
      throw new SQLException();
    }
    statement2.executeUpdate();
    connection.commit();
  } catch (SQLException e) {
    connection.rollback();
    throw e;
  }
} catch (Exception e) {
  e.printStackTrace();
}

参考情報

Discussion