@Transactional
を使用する
トランザクション構成への XML ベースの宣言的アプローチに加えて、アノテーションベースのアプローチを使用できます。Java ソースコードでトランザクションセマンティクスを直接宣言すると、影響を受けるコードにより近い宣言になります。過度な結合の危険性はあまりありません。トランザクションで使用することを目的としたコードは、ほとんどの場合、そのようにデプロイされるためです。
標準の jakarta.transaction.Transactional アノテーションは、Spring 独自のアノテーションのドロップイン置換としてもサポートされています。詳細については、JTA のドキュメントを参照してください。 |
@Transactional
アノテーションを使用することで得られる使いやすさは、以下のテキストで説明されている例で最もよく説明されています。次のクラス定義を検討してください。
Java
Kotlin
// the service class that we want to make transactional
@Transactional
public class DefaultFooService implements FooService {
@Override
public Foo getFoo(String fooName) {
// ...
}
@Override
public Foo getFoo(String fooName, String barName) {
// ...
}
@Override
public void insertFoo(Foo foo) {
// ...
}
@Override
public void updateFoo(Foo foo) {
// ...
}
}
// the service class that we want to make transactional
@Transactional
class DefaultFooService : FooService {
override fun getFoo(fooName: String): Foo {
// ...
}
override fun getFoo(fooName: String, barName: String): Foo {
// ...
}
override fun insertFoo(foo: Foo) {
// ...
}
override fun updateFoo(foo: Foo) {
// ...
}
}
上記のようにクラスレベルで使用されるアノテーションは、宣言クラス (およびそのサブクラス) のすべてのメソッドのデフォルトを示します。あるいは、各メソッドに個別にアノテーションを付けることもできます。Spring がどのメソッドをトランザクションとみなしているかの詳細については、「メソッドの可視性」を参照してください。クラスレベルのアノテーションは、クラス階層の上の祖先クラスには適用されないことに注意してください。このようなシナリオでは、サブクラスレベルのアノテーションに参加するために、継承されたメソッドをローカルで再宣言する必要があります。
上記のような POJO クラスが Spring コンテキストで Bean として定義されている場合、Bean インスタンスを @Configuration
クラスの @EnableTransactionManagement
アノテーションを介してトランザクション対応にすることができます。詳細については、javadoc を参照してください。
XML 構成では、<tx:annotation-driven/>
タグは同様の利便性を提供します。
<!-- from the file 'context.xml' -->
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- this is the service object that we want to make transactional -->
<bean id="fooService" class="x.y.service.DefaultFooService"/>
<!-- enable the configuration of transactional behavior based on annotations -->
<!-- a TransactionManager is still required -->
<tx:annotation-driven transaction-manager="txManager"/> (1)
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- (this dependency is defined somewhere else) -->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- other <bean/> definitions here -->
</beans>
1 | Bean インスタンスをトランザクション化する行。 |
接続する TransactionManager の Bean 名の名前が transactionManager の場合は、<tx:annotation-driven/> タグの transaction-manager 属性を省略できます。依存性注入する TransactionManager Bean に他の名前がある場合は、前の例のように transaction-manager 属性を使用する必要があります。 |
リアクティブトランザクションメソッドは、次のように、命令型プログラミングの配置とは対照的に、リアクティブ戻り値の型を使用します。
Java
Kotlin
// the reactive service class that we want to make transactional
@Transactional
public class DefaultFooService implements FooService {
@Override
public Publisher<Foo> getFoo(String fooName) {
// ...
}
@Override
public Mono<Foo> getFoo(String fooName, String barName) {
// ...
}
@Override
public Mono<Void> insertFoo(Foo foo) {
// ...
}
@Override
public Mono<Void> updateFoo(Foo foo) {
// ...
}
}
// the reactive service class that we want to make transactional
@Transactional
class DefaultFooService : FooService {
override fun getFoo(fooName: String): Flow<Foo> {
// ...
}
override fun getFoo(fooName: String, barName: String): Mono<Foo> {
// ...
}
override fun insertFoo(foo: Foo): Mono<Void> {
// ...
}
override fun updateFoo(foo: Foo): Mono<Void> {
// ...
}
}
Reactive Streams キャンセルシグナルに関して、返される Publisher
には特別な考慮事項があることに注意してください。詳細については、「TransactionalOperator の使用」のシグナルをキャンセルセクションを参照してください。
プロキシモードでのメソッドの可視性と @Transactional
さまざまな種類のプロキシ間でメソッドの可視性を一貫して処理したい場合 (5.3 まではデフォルトでした)、
Spring TestContext フレームワークは、非プライベート |
@Transactional
アノテーションは、インターフェース定義、インターフェース上のメソッド、クラス定義、クラス上のメソッドに適用できます。ただし、@Transactional
アノテーションが存在するだけでは、トランザクション動作をアクティブにするのに十分ではありません。@Transactional
アノテーションは、そのメタデータを使用してトランザクション動作を伴う適切な Bean を構成する、対応するランタイムインフラストラクチャによって使用できる単なるメタデータです。前述の例では、<tx:annotation-driven/>
要素は実行時に実際のトランザクション管理をオンにします。
Spring チームは、5.0 の時点でインターフェースベースのプロキシとターゲットクラスのプロキシに対して後者が機能する場合でも、インターフェース内のアノテーション付きメソッドに依存するのではなく、具象クラスのメソッドに @Transactional アノテーションを付けることをお勧めします。Java アノテーションはインターフェースから継承されないため、AspectJ モードを使用する場合、インターフェースで宣言されたアノテーションは依然としてウィービングインフラストラクチャによって認識されず、そのためアスペクトは適用されません。その結果、トランザクションのアノテーションが確認なしで無視される可能性があります。ロールバックシナリオをテストするまで、コードは「動作している」ように見える可能性があります。 |
プロキシモード (デフォルト) では、プロキシ経由で受信する外部メソッド呼び出しのみがインターセプトされます。これは、自己呼び出し (実際には、ターゲットオブジェクト内のメソッドがターゲットオブジェクトの別のメソッドを呼び出すこと) は、呼び出されたメソッドが @Transactional でマークされている場合でも、実行時に実際のトランザクションにつながらないことを意味します。また、期待される動作を提供するにはプロキシを完全に初期化する必要があるため、初期化コードでこの機能に依存しないでください。例: @PostConstruct メソッド。 |
自己呼び出しもトランザクションでラップされることが予想される場合は、AspectJ モード(次の表の mode
属性を参照)の使用を検討してください。この場合、そもそもプロキシはありません。代わりに、ターゲットクラスは、あらゆる種類のメソッドで @Transactional
ランタイム動作をサポートするように織り込まれています(つまり、そのバイトコードが変更されています)。
XML 属性 | アノテーション属性 | デフォルト | 説明 |
---|---|---|---|
| なし ( |
| 使用するトランザクションマネージャーの名前。前の例のように、トランザクションマネージャーの名前が |
|
|
| デフォルトモード( |
|
|
|
|
|
|
|
|
@Transactional アノテーションを処理するためのデフォルトのアドバイスモードは proxy です。これにより、プロキシのみを介した呼び出しのインターセプトが可能になります。同じクラス内のローカル呼び出しは、そのようにインターセプトすることはできません。より高度なインターセプトモードについては、コンパイル時またはロード時のウィービングと組み合わせて aspectj モードに切り替えることを検討してください。 |
proxy-target-class 属性は、@Transactional アノテーションが付けられたクラスに対して作成されるトランザクションプロキシの型を制御します。proxy-target-class が true に設定されている場合、クラスベースのプロキシが作成されます。proxy-target-class が false の場合、または属性が省略された場合、標準の JDK インターフェースベースのプロキシが作成されます。(さまざまなプロキシ型の説明については、プロキシメカニズムを参照してください。) |
@EnableTransactionManagement と <tx:annotation-driven/> は、それらが定義されているのと同じアプリケーションコンテキスト内の Bean でのみ @Transactional を検索します。つまり、DispatcherServlet の WebApplicationContext にアノテーション駆動型構成を配置すると、サービスではなくコントローラーでのみ @Transactional Bean がチェックされます。詳細については、MVC を参照してください。 |
メソッドのトランザクション設定を評価する場合、最も派生した場所が優先されます。次の例の場合、DefaultFooService
クラスは読み取り専用トランザクションの設定でクラスレベルでアノテーションが付けられますが、同じクラスの updateFoo(Foo)
メソッドの @Transactional
アノテーションはクラスレベルで定義されたトランザクション設定より優先されます。
Java
Kotlin
@Transactional(readOnly = true)
public class DefaultFooService implements FooService {
public Foo getFoo(String fooName) {
// ...
}
// these settings have precedence for this method
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void updateFoo(Foo foo) {
// ...
}
}
@Transactional(readOnly = true)
class DefaultFooService : FooService {
override fun getFoo(fooName: String): Foo {
// ...
}
// these settings have precedence for this method
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
override fun updateFoo(foo: Foo) {
// ...
}
}
@Transactional
の設定
@Transactional
アノテーションは、インターフェース、クラス、メソッドにトランザクションセマンティクスが必要であることを指定するメタデータです(たとえば、「このメソッドが呼び出されたときに新しい読み取り専用トランザクションを開始し、既存のトランザクションを一時停止します」)。デフォルトの @Transactional
設定は次のとおりです。
伝播設定は
PROPAGATION_REQUIRED.
です分離レベルは
ISOLATION_DEFAULT.
ですトランザクションは読み書き可能です。
トランザクションタイムアウトは、基本となるトランザクションシステムのデフォルトタイムアウトにデフォルト設定されます。タイムアウトがサポートされていない場合は、なしに設定されます。
RuntimeException
またはError
はロールバックをトリガーしますが、チェックされたException
はトリガーしません。
これらのデフォルト設定を変更できます。次の表は、@Transactional
アノテーションのさまざまなプロパティをまとめたものです。
プロパティ | タイプ | 説明 |
---|---|---|
| 使用するトランザクションマネージャーを指定するオプションの修飾子。 | |
|
|
|
| トランザクションに表現力豊かな説明を追加する | ラベルは、実装固有の動作を実際のトランザクションに関連付けるためにトランザクションマネージャーによって評価される場合があります。 |
| オプションの伝播設定。 | |
|
| オプションの分離レベル。 |
|
|
Optional transaction timeout. Applies only to propagation values of |
|
|
Alternative for specifying the |
|
| 読み取り / 書き込みトランザクションと読み取り専用トランザクション。 |
|
| ロールバックを引き起こす必要がある例外型のオプションの配列。 |
| 例外名パターンの配列。 | ロールバックを引き起こす必要がある例外名パターンのオプションの配列。 |
|
| ロールバックを引き起こしてはならない例外型のオプションの配列。 |
| 例外名パターンの配列。 | ロールバックを引き起こしてはならない例外名パターンのオプションの配列。 |
ロールバックルールのセマンティクス、パターン、パターンベースのロールバックルールの意図しない一致の可能性に関する警告の詳細については、ロールバックルールを参照してください。 |
現在、トランザクションの名前を明示的に制御することはできません。「名前」とは、トランザクションモニターおよびログ出力に表示されるトランザクション名を意味します。宣言型トランザクションの場合、トランザクション名は常に、完全修飾クラス名 + .
+ トランザクション的に推奨されるクラスのメソッド名です。例: BusinessService
クラスの handlePayment(..)
メソッドがトランザクションを開始した場合、トランザクションの名前は com.example.BusinessService.handlePayment
になります。
@Transactional
を使用した複数のトランザクションマネージャー
ほとんどの Spring アプリケーションには単一のトランザクションマネージャーのみが必要ですが、単一のアプリケーションに複数の独立したトランザクションマネージャーが必要な場合があります。@Transactional
アノテーションの value
または transactionManager
属性を使用して、オプションで、使用する TransactionManager
の ID を指定できます。これは、Bean 名またはトランザクションマネージャー Bean の修飾子値のいずれかです。例: 修飾子表記を使用して、次の Java コードをアプリケーションコンテキストで次のトランザクションマネージャー Bean 宣言と組み合わせることができます。
Java
Kotlin
public class TransactionalService {
@Transactional("order")
public void setSomething(String name) { ... }
@Transactional("account")
public void doSomething() { ... }
@Transactional("reactive-account")
public Mono<Void> doSomethingReactive() { ... }
}
class TransactionalService {
@Transactional("order")
fun setSomething(name: String) {
// ...
}
@Transactional("account")
fun doSomething() {
// ...
}
@Transactional("reactive-account")
fun doSomethingReactive(): Mono<Void> {
// ...
}
}
以下のリストは、Bean 宣言を示しています。
<tx:annotation-driven/>
<bean id="transactionManager1" class="org.springframework.jdbc.support.JdbcTransactionManager">
...
<qualifier value="order"/>
</bean>
<bean id="transactionManager2" class="org.springframework.jdbc.support.JdbcTransactionManager">
...
<qualifier value="account"/>
</bean>
<bean id="transactionManager3" class="org.springframework.data.r2dbc.connection.R2dbcTransactionManager">
...
<qualifier value="reactive-account"/>
</bean>
この場合、TransactionalService
の個々のメソッドは、order
、account
、reactive-account
修飾子によって区別された個別のトランザクションマネージャーで実行されます。特に修飾された TransactionManager
Bean が見つからない場合でも、デフォルトの <tx:annotation-driven>
ターゲット Bean 名 transactionManager
が引き続き使用されます。
カスタム構成アノテーション
多くの異なるメソッドで @Transactional
で同じ属性を繰り返し使用していることがわかった場合、Spring のメタアノテーションサポートを使用すると、特定のユースケースに合わせてカスタム合成アノテーションを定義できます。例: 次のアノテーション定義を検討します。
Java
Kotlin
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional(transactionManager = "order", label = "causal-consistency")
public @interface OrderTx {
}
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional(transactionManager = "account", label = "retryable")
public @interface AccountTx {
}
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
@Transactional(transactionManager = "order", label = ["causal-consistency"])
annotation class OrderTx
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
@Transactional(transactionManager = "account", label = ["retryable"])
annotation class AccountTx
前述のアノテーションにより、前のセクションの例を次のように記述できます。
Java
Kotlin
public class TransactionalService {
@OrderTx
public void setSomething(String name) {
// ...
}
@AccountTx
public void doSomething() {
// ...
}
}
class TransactionalService {
@OrderTx
fun setSomething(name: String) {
// ...
}
@AccountTx
fun doSomething() {
// ...
}
}
前の例では、構文を使用してトランザクションマネージャー修飾子とトランザクションラベルを定義しましたが、伝播動作、ロールバックルール、タイムアウト、その他の機能を含めることもできます。