バージョンはSpring4.2.X系
Springでのトランザクション管理
Springのトランザクション管理は大きく分けて以下の2つの管理方法が存在する。
1.プログラミングによるトランザクション管理
プログラミングによってトランザクションを手動管理する方法。以下のようなイメージでトランザクション制御コードをソースコード内に記述する。
transactionManager.begin();
transactionManager.commit();
transactionManager.rollback();
ソースコード内にトランザクション制御コードも入りこむため、見通しが悪くなったり制御を間違えるとバグの温床にもなったりするので、極力使うべきではない。
2.宣言的トランザクション管理
「あるメソッドを呼び出したときにトランザクションをかける」と宣言する方法。以下のようなイメージでメソッド(やクラスに)に@Transactionalアノテーションを付与することで実現される。
以下の例では、updateFooメソッドを呼び出したときにトランザクションをかける、ということ。
※XMLベースでも宣言可能だがここではアノテーションベースを利用する
public class DefaultFooService implements FooService {
public Foo getFoo(String fooName) {
// do something
}
// these settings have precedence for this method
@Transactional
public void updateFoo(Foo foo) {
// do something
}
}
トランザクション制御コードがソースコード内に入りこむことがないため、この管理方法が推奨されている。
詳細は後述するが、メソッドやクラスに@Transactionalアノテーションを付与することで管理され、トランザクションの開始、コミット、ロールバックは自動で行われる。
ただし、ロールバックの注意点として、非検査例外(RuntimeException及びそのサブクラス)が発生した場合はロールバックされるが、検査例外(Exception及びそのサブクラスでRuntimeExceptionのサブクラスじゃないもの)が発生した場合はロールバックされずコミットされる。
なお、Springの宣言的トランザクションはAOPを使って実現されている。
※AOPについてはSpringでAOPを参照。
本記事では「2.宣言的トランザクション管理」について明記する。
トランザクションマネージャ
Springではいくつかのトランザクションマネージャが用意されている。
これらは利用するDBアクセスAPIによって使い分ける。
DBアクセスAPI | トランザクションマネージャ |
---|---|
JDBC | org.springframework.jdbc.datasource.DataSourceTransactionManager |
JDO | org.springframework.orm.jdo.JdoTransactionManager |
JTA | org.springframework.transaction.jta.JtaTransactionManager |
Hibernate | org.springframework.orm.hibernate.HibernateTransactionManager |
トランザクション制御
冒頭でも述べたが、以下のようにメソッドにTransactionalアノテーションを付与する方法とクラスにTransactionalアノテーションを付与する方法がある。
メソッドにTransactionalアノテーションを付与する
メソッドにTransactionalアノテーション付与することで、メソッドが呼ばれたタイミング(正確にはメソッド開始前)にトランザクションが開始され、対象のメソッドが正常終了した場合はコミット、例外で終了した場合はロールバックされるようになる(再掲になるがロールバックの注意点として、非検査例外(RuntimeException及びそのサブクラス)が発生した場合はロールバックされるが、検査例外(Exception及びそのサブクラスでRuntimeExceptionのサブクラスじゃないもの)が発生した場合はロールバックされずコミットされる)。いづれもアノテーションを付与しておくだけで自動でやってくれる。
public class DefaultFooService implements FooService {
public Foo getFoo(String fooName) {
// do something
}
// these settings have precedence for this method
@Transactional
public void updateFoo(Foo foo) {
// do something
}
}
クラスにTransactionalアノテーションを付与する
以下のようにクラスにTransactionalアノテーションを付与することで、そのクラス内の全てのメソッドにトランザクション制御をかけることができる。
@Transactional(readOnly = true)
public class DefaultFooService implements FooService {
public Foo getFoo(String fooName) {
// do something
}
// these settings have precedence for this method
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void updateFoo(Foo foo) {
// do something
}
}
ただし、クラスに対して記述した設定はメソッドで記述された設定で上書きされることに注意する必要がある。上記の場合、updateFooメソッドは@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)が適用される(クラスに付与されている@Transactional(readOnly = true)は効かない)。
なお、Transactionalアノテーションはpublicメソッドのみに適用可能。public以外のメソッドにも付与できてコンパイルも通るが、実行時エラーにもならないので注意が必要。
you should apply the @Transactional annotation only to methods with public visibility. If you do annotate protected, private or package-visible methods with the @Transactional annotation, no error is raised, but the annotated method does not exhibit the configured transactional settings. Consider the use of AspectJ (see below) if you need to annotate non-public methods.
Transactionalアノテーションのプロパティと設定例
Transactionalアノテーションに指定可能なプロパティ
プロパティ | 型 | 定義 |
---|---|---|
value | String | 1つのアプリケーションで複数のトランザクションマネージャを利用する場合に、どのトランザクションマネージャを利用するかを指定する。 |
propagation | Enum: Propagation | どのような場合にトランザクションを開始するか等の属性を指定する。 |
isolation | Enum: Isolation | トランザクションの分離レベルを指定する。デフォルトはDEFAULTレベル。 |
readOnly | boolean | 読み取り専用かどうかを指定する。デフォルトはfalse。 |
timeout | int (in seconds granularity) | そのトランザクションがタイムアウトする時間を指定する(秒指定)。デフォルトは利用しているトランザクション管理システムのタイムアウトを利用する。 |
rollbackFor | Class extends Throwable>[] | 検査例外が発生した際もロールバックしたい場合に指定する。 |
rollbackForClassName | String[] | rollbackForと同じ、違いは例外クラス名また例外クラス名の配列を指定するところ。 |
noRollbackFor | Class extends Throwable>[] | ロールバックしたくない場合に指定する。 |
noRollbackForClassName | String[] | noRollbackForと同じ、違いは例外クラス名また例外クラス名の配列を指定するところ。 |
プロパティの設定例
value
// 利用したいトランザクションマネージャの名前を指定する
@Transactional("order")
propagation
// 常に新しいトランザクションを開始する
@Transactional(propagation=Propagation.REQUIRES_NEW)
propagationのその他の設定値
プロパティ | 定義 |
---|---|
MANDATORY | トランザクションが開始されていなければ例外を発生させる。 |
NESTED | トランザクションが存在する場合はネストしたトランザクションを開始し、それ以外はREQUIREDのように振る舞う。 |
NEVER | トランザクションが開始されていれば例外を発生させる。 |
NOT_SUPPORTED | トランザクションが存在する場合は中断して、トランザクションをはらない(トランザクションを利用しない) |
REQUIRED | トランザクションが開始されていなければ新規に開始し、すでに開始されていればそのトランザクションをそのまま利用する。 |
REQUIRES_NEW | 常に新しいトランザクションを開始する。トランザクションが存在する場合は中断して新しいトランザクションを開始する。 |
SUPPORTS | トランザクションが開始されていればそれを利用。開始されていなければトランザクションをはらない(トランザクションを利用しない)。 |
isolation
// ダーティーリード・ファジーリードは発生しないが、ファントムリードは発生する可能性がある
@Transactional(isolation=Isolation.REPEATABLE_READ)
isolationのその他の設定値
※下に行くほど分離レベルが高くなる
|プロパティ|定義|
|:--|:--|:--|
|DEFAULT|利用するデータストアのデフォルト分離レベルを利用する。|
|READ_UNCOMMITTED|ダーティーリード・ファジーリード・ファントムリードが発生する可能性がある。|
|READ_COMMITTED|ダーティーリードは発生しないが、ファジーリード・ファントムリードは発生する可能性がある。|
|REPEATABLE_READ|ダーティーリード・ファジーリードは発生しないが、ファントムリードは発生する可能性がある。|
|SERIALIZABLE|ダーティーリード・ファジーリード・ファントムリードは発生する可能性がない。|
readOnly
// 読み取り専用トランザクション
@Transactional(readOnly = true)
timeout
// 10秒でタイムアウト
@Transactional(timeout = 10)
rollbackFor
// 全ての例外が発生した場合、ロールバックさせる
@Transactional(rollbackFor=Exception.class)
rollbackForClassName
// 全ての例外が発生した場合、ロールバックさせる
@Transactional(rollbackForClassName={"Exception"})
noRollbackFor
// 全ての例外が発生した場合、ロールバックさせないようにする
@Transactional(noRollbackFor=Exception.class)
noRollbackForClassName
// 全ての例外が発生した場合、ロールバックさせないようにする
@Transactional(noRollbackForClassName={"Exception"})
参考
http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/transaction/annotation/Transactional.html
http://docs.spring.io/autorepo/docs/spring/4.2.x/spring-framework-reference/html/transaction.html
https://spring.io/guides/gs/managing-transactions/
http://dev.classmethod.jp/server-side/transaction-management-in-spring/
http://masatoshitada.hatenadiary.jp/entry/2015/12/05/135825