Edited at

Springでトランザクション管理

More than 3 years have passed since last update.

バージョンは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
トランザクションが存在する場合はネストしたトランザクションを開始し、他のPROPAGATION_REQUIREDのように振る舞う。

NEVER
トランザクションが開始されていれば例外を発生させる。

NOT_SUPPORTED
トランザクションが存在する場合は中断して、トランザクションをはらない(トランザクションを利用しない)

REQUIRED
トランザクションが開始されていなければ新規に開始し、すでに開始されていればそのトランザクションをそのまま利用する。

REQUIRES_NEW
常に新しいトランザクションを開始する。トランザクションが存在する場合は中断して新しいトランザクションを開始する。

SUPPORTS
トランザクションが開始されていればそれを利用。開始されていなければトランザクションをはらない(トランザクションを利用しない)。

http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/transaction/annotation/Propagation.html


isolation


設定例.

// ダーティーリード・ファジーリードは発生しないが、ファントムリードは発生する可能性がある

@Transactional(isolation=Isolation.REPEATABLE_READ)


isolationのその他の設定値

※下に行くほど分離レベルが高くなる

プロパティ
定義

DEFAULT
利用するデータストアのデフォルト分離レベルを利用する。

READ_UNCOMMITTED
ダーティーリード・ファジーリード・ファントムリードが発生する可能性がある。

READ_COMMITTED
ダーティーリードは発生しないが、ファジーリード・ファントムリードは発生する可能性がある。

REPEATABLE_READ
ダーティーリード・ファジーリードは発生しないが、ファントムリードは発生する可能性がある。

SERIALIZABLE
ダーティーリード・ファジーリード・ファントムリードは発生する可能性がない。

https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/transaction/annotation/Isolation.html


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