たまには技術っぽいことも書いてみますかねぇ。
Java 7で追加されたtry-with-resources構文というのがあります。
主にリソースの類を扱うときには必須といってもいいもの。
この構文を使うと、正常・例外発生など問わずブロックを抜けたとき自動でcloseしてくれるものですね。
発生した例外や、クローズ処理自体で発生した例外の扱いも
非常にうまいことやってくれたりしていてかなり助かります。
自分も積極的に利用しています。当然、DB接続を必要とするときとかにも。
自分は、仕事の関係でよくOracleのDBを使います。
OracleのJDBCドライバもよく使うことになります。
直接いじるんでなかったとしても、間接的には扱っている、と。
そのOracleのJDBCドライバには、DB接続クローズ時に暗黙のコミットが実行されるという仕様があります。
setAutoCommit()でDML実行時の自動コミットをしないよう設定しても、
このクローズ時の暗黙のコミットは実行されます。
クローズ時点で未コミットだった更新が反映されるわけです。
SQLを実行して、それがエラーなく実行できたのであれば、
最終的にはそれは反映されるべきもののはずで、この仕様自体は当然といえば当然です。
ただこの仕様、try-with-resource文を使うこと、業務仕様の都合によるトランザクション処理、
そのあたりを合わせて考えると、ちょっと注意が必要な場合がありそうかなと思いました。
仕様の都合上、複数のDML文で実現をすることがけっこうあります。
そのときは、データの整合性を保つため、複数のDMLやそれに絡む処理が全部成功したら初めてコミットでき、
DML文やそのまわりの処理で例外でもでたり、不都合なことがあったりしたなら、
ちゃんとロールバックしてやらなきゃならないということがままあります。
ところが、単純に以下のように書いてしまうと困ったことに。
DMLその2で万一SQLExceptionなんかが起こってしまったりすると、
try-with-resourceのブロックを抜けてDB接続が自動クローズされます。
そのとき暗黙のコミットが走るので、DMLその1だけ反映された状態になってしまいます。
Oralce上でのエラーは、何が起こったとしてもSQLExceptionが投げられるようになってるので、
発生し次第、どうあろうとブロックを抜けてしまうわけです。
例外をキャッチした※のとこでなんとかしようとしても、この時点では暗黙のコミットは走ったあと。
DB接続もクローズ済み。そもそもスコープ外なので変数connにアクセスできず、なんともなりません。
うーん。困った。
下記のように、もいっこカン じゃなくて もいっこtryを重ねればロールバックできますが、
これあんまりかっこよくないですね…。
せっかくtry-with-resource文一本できれいだったのに、tryのために1段字下げすることに。
字下げもそうなんですが、tryを連続で2つというのが非常にもやもやします。
しますが、もっとシンプルできれいな方法というのも思いつきません。
こうするしかないのかなぁ。
Java 7で追加されたtry-with-resources構文というのがあります。
主にリソースの類を扱うときには必須といってもいいもの。
この構文を使うと、正常・例外発生など問わずブロックを抜けたとき自動でcloseしてくれるものですね。
発生した例外や、クローズ処理自体で発生した例外の扱いも
非常にうまいことやってくれたりしていてかなり助かります。
自分も積極的に利用しています。当然、DB接続を必要とするときとかにも。
自分は、仕事の関係でよくOracleのDBを使います。
OracleのJDBCドライバもよく使うことになります。
直接いじるんでなかったとしても、間接的には扱っている、と。
そのOracleのJDBCドライバには、DB接続クローズ時に暗黙のコミットが実行されるという仕様があります。
setAutoCommit()でDML実行時の自動コミットをしないよう設定しても、
このクローズ時の暗黙のコミットは実行されます。
クローズ時点で未コミットだった更新が反映されるわけです。
SQLを実行して、それがエラーなく実行できたのであれば、
最終的にはそれは反映されるべきもののはずで、この仕様自体は当然といえば当然です。
ただこの仕様、try-with-resource文を使うこと、業務仕様の都合によるトランザクション処理、
そのあたりを合わせて考えると、ちょっと注意が必要な場合がありそうかなと思いました。
仕様の都合上、複数のDML文で実現をすることがけっこうあります。
そのときは、データの整合性を保つため、複数のDMLやそれに絡む処理が全部成功したら初めてコミットでき、
DML文やそのまわりの処理で例外でもでたり、不都合なことがあったりしたなら、
ちゃんとロールバックしてやらなきゃならないということがままあります。
ところが、単純に以下のように書いてしまうと困ったことに。
try (Connection conn = ds.getConnection()) { conn.setAutoCommit(false); PreparedStatement pstmt = conn.prepareStatement(sqlHoge); // DMLその1 // 省略。パラメータ設定とか。 pstmt.executeUpdate(); PreparedStatement pstmt2 = conn.prepareStatement(sqlFuga); // DMLその2 // 省略。パラメータ設定とか。 pstmt2.executeUpdate(); conn.commit(); } catch(SQLException e) { // エラー処理(ロールバックしたい!) ※ }
DMLその2で万一SQLExceptionなんかが起こってしまったりすると、
try-with-resourceのブロックを抜けてDB接続が自動クローズされます。
そのとき暗黙のコミットが走るので、DMLその1だけ反映された状態になってしまいます。
Oralce上でのエラーは、何が起こったとしてもSQLExceptionが投げられるようになってるので、
発生し次第、どうあろうとブロックを抜けてしまうわけです。
例外をキャッチした※のとこでなんとかしようとしても、この時点では暗黙のコミットは走ったあと。
DB接続もクローズ済み。そもそもスコープ外なので変数connにアクセスできず、なんともなりません。
うーん。困った。
下記のように、もいっこカン じゃなくて もいっこtryを重ねればロールバックできますが、
これあんまりかっこよくないですね…。
try (Connection conn = ds.getConnection()) { try { conn.setAutoCommit(false); PreparedStatement pstmt = conn.prepareStatement(sqlHoge); // DMLその1 // パラメータ設定とか? pstmt.executeUpdate(); // なんか処理 PreparedStatement pstmt2 = conn.prepareStatement(sqlFuga); // DMLその2 // パラメータ設定とか? pstmt2.executeUpdate(); conn.commit(); } catch(SQLException e) { // エラー処理 conn.rollback(); } }
せっかくtry-with-resource文一本できれいだったのに、tryのために1段字下げすることに。
字下げもそうなんですが、tryを連続で2つというのが非常にもやもやします。
しますが、もっとシンプルできれいな方法というのも思いつきません。
こうするしかないのかなぁ。