java

try-with-resources文

レガシーなAPI(JDBC接続)プログラム(Java 1.6時代)が無理やりSpring Bootに載せ替えられて動いていたのですが、本日不具合をみつけ、try-with-resources文を使って修正したので作業メモとして残します。

調べてみるとjava 7(2011年)からのようですが、try-with-resources が導入され、JDBC リソースを自動で close できるようになっています。この構文はJDBC専用というわけでなく、closeが必要なリソース=「AutoCloseableを実装したリソース」全般で利用できます。ここではjdbcを例に解説します

Java6以前

こんな感じでcloseがめんどいし、close漏れが何かと重大な不具合の元になっていました。

Connection conn = null;
PreparedStatement ps = null;
try {
    conn = dataSource.getConnection();
  conn.setAutoCommit(true);
    ps = conn.prepareStatement("SELECT ...");
    // 処理...
} catch (SQLException e) {
    // エラーハンドリング
} finally {
    // 閉じる順番を気にしたり、nullチェックが必要でとてもめんどい
    if (ps != null) {
        try { ps.close(); } catch (SQLException e) { /* スルー */ }
    }
    if (conn != null) {
        try { conn.close(); } catch (SQLException e) { /* スルー */ }
    }
}
Java7からはかなりスマートに

注意:JDBCの仕様として、setAutoCommit(true)がデフォルトなのであまり意識ないかもですが、setAutoCommit(false)にする場合は、これまで通りcommit/rollbackは自分で入れないといけません。あくまでcloseが省略されるだけです。

// try の後ろの ( ) の中でリソースを生成する
try (Connection conn = dataSource.getConnection();
     PreparedStatement ps = conn.prepareStatement("SELECT ...")) {
    
    // ここに本質的な処理を書くだけ!
    
} catch (SQLException e) {
    // エラーハンドリング(クローズは「この前」に自動で終わっている)
}
// finally を書かなくても、スコープを抜けた時点で ps と conn が自動で close() される

tryの後ろを()で囲み、その中にはAutoCloseable(または Closeable)というインターフェースを実装しているクラスの変数宣言だけ(closeが必要なリソース)記載する事ができます。そのためconn.setAutoCommit(true); などをここに入れる事ができません。これを対策するには以下3つほど考えてみました。

案1 tryを2段構えとする。

せっかくシンプルになるところtryの2段構えはいまいちかなぁ、、

try (Connection conn = dataSource.getConnection()) {
    
    // こだわり貫徹:psを作る「前」に設定を完了させる!
    conn.setAutoCommit(true); 

    try (PreparedStatement ps = conn.prepareStatement("SELECT ...")) {
        ps.executeQuery();
    }
}
案2 気持ち悪いけどpreparestatementの後に設定する

executeの前に設定すればよいと割り切るか、、、

try (Connection conn = dataSource.getConnection();
     PreparedStatement ps = conn.prepareStatement("SELECT ...")) {
    
    // 妥協案:psを作った「後」、実行する「前」に設定する
    conn.setAutoCommit(true); 
    
    ps.executeQuery();
}
案3 ヘルパーメソッドでごまかす(関数で包んでしまう)
try (Connection conn = enableAutoCommit(dataSource.getConnection()); // 関数で包む!
     PreparedStatement ps = conn.prepareStatement("SELECT ...")) {
    
    // ここに本質的な処理を書くだけ!
    
} catch (SQLException e) { ... }

// Connectionの設定を済ませてそのままConnectionを返すヘルパーメソッドを作成
private Connection enableAutoCommit(Connection conn) throws SQLException {
    conn.setAutoCommit(true); // ここで設定変更
    return conn;              // 変更済みのconnをそのまま次に引き渡す
}

スポンサーリンク
タイトルとURLをコピーしました