Help us understand the problem. What is going on with this article?

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away