Springで管理されているサービスメソッドを使用して、データベースの挿入を管理しています。複数の挿入ステートメントが含まれています。
@Transactional
public void insertObservation(ObservationWithData ob) throws SQLException
{
observationDao.insertObservation(ob.getObservation());
// aop pointcut inserted here in unit test
dataDao.insertData(ob.getData());
}
2番目の挿入を呼び出す前に例外をスローする2つの単体テストがあります。例外がRuntimeExceptionの場合、トランザクションはロールバックされます。例外がSQLExceptionの場合、最初の挿入は永続化されます。
私は困惑しています。トランザクションがSQLExceptionでロールバックしない理由を誰かに教えてもらえますか?誰でもこれを管理する方法を提案できますか? SQLExceptionをキャッチして、RuntimeExceptionをスローすることもできましたが、それは奇妙に思えます。
これは定義された動作です。 docs から:
RuntimeException
はロールバックをトリガーし、チェックされた例外はトリガーしません。
これは、すべてのSpringトランザクションAPIで共通の動作です。デフォルトでは、トランザクションコード内からRuntimeException
がスローされると、トランザクションはロールバックされます。チェックされた例外(つまり、RuntimeException
ではない)がスローされた場合、トランザクションはロールバックされません。
これの背後にある理論的根拠は、RuntimeException
クラスは通常、回復不可能なエラー状態を示すためにSpringによって取得されるということです。
この動作は、必要に応じてデフォルトから変更できますが、その方法は、Spring APIの使用方法とトランザクションマネージャーの設定方法によって異なります。
ために @Transactional
、デフォルトでは、ロールバックは実行時に発生し、チェックされていない例外のみが発生します。したがって、チェックされた例外SQLException
はトランザクションのロールバックをトリガーしません。動作は、rollbackFor
およびnoRollbackFor
注釈パラメーターを使用して構成できます。
@Transactional(rollbackFor = SQLException.class)
public void insertObservation(ObservationWithData ob) throws SQLException
{
observationDao.insertObservation(ob.getObservation());
// aop pointcut inserted here in unit test
dataDao.insertData(ob.getData());
}
Springは、例外から回復できない場合に、RuntimeExceptions(DataAccessExceptionsを使用してSQLExceptionsまたはORMからの例外をラップすることを含む)を広範囲に使用します。サービスの上の層に何かを通知する必要があるが、トランザクションに干渉されないようにする場合は、チェック例外を使用することを想定しています。
Springを使用している場合は、jdbc-wrappingライブラリとDataAccessException変換機能を利用することもできます。これにより、維持しなければならないコードの量が減り、より意味のある例外が提供されます。また、実装固有の例外をスローするサービスレイヤーがあることは悪臭です。春以前の方法は、実装に依存しないチェック例外を作成して実装固有の例外をラップすることでしたが、これにより多くの作業が忙しくなり、コードベースが肥大化しました。春の方法はこれらの問題を回避します。
Springが物事をこのように機能させることを選択した理由を知りたい場合は、おそらくAOPを使用してトランザクション処理を追加しているためです。ラップしているメソッドのシグネチャを変更することはできないため、チェックされた例外はオプションではありません。