━目次━






プロジェクトでTERASOLUNA(Spring Frameworkをラップしているフレームワーク)を使っていたのですが、

開発が進んできたときにあることが話題になりました。


@Transactionalでのトランザクション管理はデフォルトでは検査例外をコミットする!

考える人考える人

なんだと~!(遅い)

そこからSpringのトランザクション管理の仕組みについて色々調べたのでまとめます。

検査例外と非検査例外については別記事にしましたので合わせて読んでみて下さい
 【Java】検査例外と非検査例外の違いを図でまとめてみた


@Transactionalのオプション一覧

初めに@Transactionalの選択可能な属性を見てみます。

属性名 設定値 説明 デフォルト
propagation
トランザクションの伝播方法を指定する。
REQUIRED トランザクションが開始されていなければ開始する。
REQUIRES_NEW 常に新しいトランザクションを開始する。
SUPPORTS トランザクションが開始されていればそれを利用する。
開始されていなければ利用しない。
NOT_SUPPORTED トランザクションを利用しない。
MANDATORY トランザクションが開始されていなければ例外が発生する。
NEVER トランザクションが開始していれば例外が発生する。
NESTED セーブポイントが設定される。JDBCのみ有効。
isolation
トランザクションの独立レベルを指定する。
DBの仕様に依存
DEFAULT DBが提供するデフォルトの独立性レベル。 ○(↓参照)
READ_UNCOMMITTED 他のトランザクションで変更中(未コミット)のデータが読める。
READ_COMMITTED 他のトランザクションで変更中(未コミット)のデータは読めない。 Oracle,DB2,PostgreSQL,SQL Server
REPEATABLE_READ 他のトランザクションが読み出したデータは更新できない。 MySQL
SERIALIZABLE トランザクションを完全に独立させる。
timeout 時間(秒) トランザクションのタイムアウト時間(秒)を指定。 -1
readOnly true/false トランザクションの読取専用フラグを指定。 false
rollbackFor クラスリスト トランザクションのロールバック対象とする例外クラスのリストを指定。 指定なし
rollbackForClassName クラス名リスト rollbackForのクラス名バージョン 指定なし
noRollbackFor クラスリスト トランザクションのコミット対象とする例外クラスのリストを指定。 指定なし
noRollbackForClassName クラス名リスト noRollbackForのクラス名バージョン 指定なし



基本的にpropagationは指定なし(デフォルトのREQUIRED)にしておくのが一般的でしょう。
(@Transactionalを指定したサービスクラスを入れ子に実行しても、例外が発生した時は全てロールバックされる)

isolationは主要なDBだとMySQLだけ挙動が異なるので注意が必要です。

デフォルトでは検査例外はコミットされる!

先程の@Transactionalのロールバック関係のオプション(rollbackFor,rollbackForClassName,noRollbackFor,noRollbackForClassName)を指定しない場合、Spring Frameworkはデフォルトで以下の動作をします。


●非検査例外クラス(java.lang.RuntimeExceptionおよびjava.lang.Error)またはそのサブクラスの例外が発生
 ロールバック

●検査例外クラス(java.lang.Exception)またはそのサブクラス(java.lang.RuntimeExceptionを除く)の例外が発生
 コミット




検査例外にはIOException(java.io.IOException)SQLException(java.sql.SQLException)も含まれます。

DB関係の例外でロールバックされないのはまずい!と思って挙動を確認しましたが、



あれ?SQLExceptionを発生させてもロールバックされるぞ??


んん?IOExceptionを発生させるとコミットされている…


1番上のExceptionクラスを発生させると…コミットされる…



何故か検査例外の中でもSQLExceptionだけ記述と違い、ロールバックされてしまう事象が起きました。

色々調べてみると、Spring Frameworkの仕様に謎が隠されていました。

Spring FrameworkではSQLExceptionをspring独自のExceptionに変換している!

ある記述を見つけました

Spring Frameworkでは、JDBCの例外(java.sql.SQLException)や、O/R Mapper固有の例外を、Spring Frameworkから提供しているデータアクセス例外(org.springframework.dao.DataAccessExceptionのサブクラス)に変換する機能がある。

なるほど、納得です。

SQLExceptionが発生した場合、Spring Frameworkが内部的にDataAccessException系に変換しているため、検査例外と認識されずにロールバックされていたんですね。

たぶんSQL系の例外は良くあるから開発者がハンドリングを意識しないで良いようにっていう思想でこうなったのかな?


考える人考える人

もうそれだったら検査例外もデフォルトで全部ロールバックにしといて欲しいけどのぉ~

検査例外も含め全てロールバックするコード

因みに検査例外も含めて全ての例外発生時にロールバックしたい場合は以下のように記述すればOKです。

さいごに

いやー時間とられました。

というかたぶんSpring Framework開発者なら知ってて当然の知識かもしれませんが、何分初Springだったもので…

正直例外の検査・非検査なんて意識したくないのでそこはJavaの「ん~~」ってとこですね。

他の言語も採用しなかったみたいですし…

それでは!また!!

最後まで読んで頂きありがとうございます!
人気の記事だけ集めたので是非覗いていってください^^
 厳選!目的別にオススメ記事を紹介-あなたの欲しい情報がここに-