プログラムによるトランザクション管理
Spring Framework は、以下を使用することにより、プログラムによるトランザクション管理の 2 つの手段を提供します。
TransactionTemplate
またはTransactionalOperator
。TransactionManager
実装。
Spring チームは通常、命令フローでのプログラムによるトランザクション管理には TransactionTemplate
を、リアクティブコードには TransactionalOperator
を推奨しています。2 番目のアプローチは、JTA UserTransaction
API を使用する場合と似ていますが、例外処理はそれほど面倒ではありません。
TransactionTemplate
を使用する
TransactionTemplate
は、JdbcTemplate
などの他の Spring テンプレートと同じアプローチを採用しています。コールバックアプローチ(ボイラープレートの取得を実行してトランザクションリソースを解放する必要からアプリケーションコードを解放する)を使用し、コードが実行したいことにのみ焦点を当てるという点で、意図に基づいたコードになります。
以下の例が示すように、TransactionTemplate を使用すると、Spring のトランザクションインフラストラクチャと API に完全に結合されます。プログラマティックトランザクション管理が開発ニーズに適しているかどうかは、あなた自身で決定する必要があります。 |
トランザクションコンテキストで実行する必要があり、TransactionTemplate
を明示的に使用するアプリケーションコードは、次の例のようになります。アプリケーション開発者は、トランザクションのコンテキストで実行する必要があるコードを含む TransactionCallback
実装(通常は匿名の内部クラスとして表現)を作成できます。次に、カスタム TransactionCallback
のインスタンスを TransactionTemplate
で公開されている execute(..)
メソッドに渡すことができます。次の例は、その方法を示しています。
Java
Kotlin
public class SimpleService implements Service {
// single TransactionTemplate shared amongst all methods in this instance
private final TransactionTemplate transactionTemplate;
// use constructor-injection to supply the PlatformTransactionManager
public SimpleService(PlatformTransactionManager transactionManager) {
this.transactionTemplate = new TransactionTemplate(transactionManager);
}
public Object someServiceMethod() {
return transactionTemplate.execute(new TransactionCallback() {
// the code in this method runs in a transactional context
public Object doInTransaction(TransactionStatus status) {
updateOperation1();
return resultOfUpdateOperation2();
}
});
}
}
// use constructor-injection to supply the PlatformTransactionManager
class SimpleService(transactionManager: PlatformTransactionManager) : Service {
// single TransactionTemplate shared amongst all methods in this instance
private val transactionTemplate = TransactionTemplate(transactionManager)
fun someServiceMethod() = transactionTemplate.execute<Any?> {
updateOperation1()
resultOfUpdateOperation2()
}
}
戻り値がない場合、次のように匿名クラスで便利な TransactionCallbackWithoutResult
クラスを使用できます。
Java
Kotlin
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
updateOperation1();
updateOperation2();
}
});
transactionTemplate.execute(object : TransactionCallbackWithoutResult() {
override fun doInTransactionWithoutResult(status: TransactionStatus) {
updateOperation1()
updateOperation2()
}
})
コールバック内のコードは、次のように、提供された TransactionStatus
オブジェクトで setRollbackOnly()
メソッドを呼び出すことにより、トランザクションをロールバックできます。
Java
Kotlin
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
protected void doInTransactionWithoutResult(TransactionStatus status) {
try {
updateOperation1();
updateOperation2();
} catch (SomeBusinessException ex) {
status.setRollbackOnly();
}
}
});
transactionTemplate.execute(object : TransactionCallbackWithoutResult() {
override fun doInTransactionWithoutResult(status: TransactionStatus) {
try {
updateOperation1()
updateOperation2()
} catch (ex: SomeBusinessException) {
status.setRollbackOnly()
}
}
})
トランザクション設定の指定
TransactionTemplate
のトランザクション設定 (伝播モード、分離レベル、タイムアウトなど) は、プログラムまたは構成で指定できます。デフォルトでは、TransactionTemplate
インスタンスにはデフォルトのトランザクション設定が適用されます。次の例は、特定の TransactionTemplate:
のトランザクション設定のプログラムによるカスタマイズを示しています。
Java
Kotlin
public class SimpleService implements Service {
private final TransactionTemplate transactionTemplate;
public SimpleService(PlatformTransactionManager transactionManager) {
this.transactionTemplate = new TransactionTemplate(transactionManager);
// the transaction settings can be set here explicitly if so desired
this.transactionTemplate.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED);
this.transactionTemplate.setTimeout(30); // 30 seconds
// and so forth...
}
}
class SimpleService(transactionManager: PlatformTransactionManager) : Service {
private val transactionTemplate = TransactionTemplate(transactionManager).apply {
// the transaction settings can be set here explicitly if so desired
isolationLevel = TransactionDefinition.ISOLATION_READ_UNCOMMITTED
timeout = 30 // 30 seconds
// and so forth...
}
}
次の例では、Spring XML 構成を使用して、いくつかのカスタムトランザクション設定で TransactionTemplate
を定義しています。
<bean id="sharedTransactionTemplate"
class="org.springframework.transaction.support.TransactionTemplate">
<property name="isolationLevelName" value="ISOLATION_READ_UNCOMMITTED"/>
<property name="timeout" value="30"/>
</bean>
その後、sharedTransactionTemplate
を必要な数のサービスに挿入できます。
最後に、TransactionTemplate
クラスのインスタンスはスレッドセーフであり、そのインスタンスは会話状態を維持しません。ただし、TransactionTemplate
インスタンスは構成状態を維持します。そのため、多くのクラスが TransactionTemplate
の単一のインスタンスを共有する場合がありますが、クラスが異なる設定(たとえば、異なる分離レベル)で TransactionTemplate
を使用する必要がある場合、2 つの異なる TransactionTemplate
インスタンスを作成する必要があります。
TransactionalOperator
を使用する
TransactionalOperator
は、他のリアクティブオペレーターと同様のオペレーター設計に従います。コールバックアプローチ(ボイラープレートの取得を実行してトランザクションリソースを解放する必要からアプリケーションコードを解放する)を使用し、コードが実行したいことにのみ焦点を当てるという点で、意図に基づいたコードになります。
以下の例が示すように、TransactionalOperator を使用すると、Spring のトランザクションインフラストラクチャと API に完全に結合されます。プログラマティックトランザクション管理が開発ニーズに適しているかどうかは、あなた自身で決定する必要があります。 |
トランザクションコンテキストで実行する必要があり、TransactionalOperator
を明示的に使用するアプリケーションコードは、次の例のようになります。
Java
Kotlin
public class SimpleService implements Service {
// single TransactionalOperator shared amongst all methods in this instance
private final TransactionalOperator transactionalOperator;
// use constructor-injection to supply the ReactiveTransactionManager
public SimpleService(ReactiveTransactionManager transactionManager) {
this.transactionalOperator = TransactionalOperator.create(transactionManager);
}
public Mono<Object> someServiceMethod() {
// the code in this method runs in a transactional context
Mono<Object> update = updateOperation1();
return update.then(resultOfUpdateOperation2).as(transactionalOperator::transactional);
}
}
// use constructor-injection to supply the ReactiveTransactionManager
class SimpleService(transactionManager: ReactiveTransactionManager) : Service {
// single TransactionalOperator shared amongst all methods in this instance
private val transactionalOperator = TransactionalOperator.create(transactionManager)
suspend fun someServiceMethod() = transactionalOperator.executeAndAwait<Any?> {
updateOperation1()
resultOfUpdateOperation2()
}
}
TransactionalOperator
は 2 つの方法で使用できます。
プロジェクト Reactor 型を使用するオペレータースタイル (
mono.as(transactionalOperator::transactional)
)他のすべての場合のコールバックスタイル (
transactionalOperator.execute(TransactionCallback<T>)
)
コールバック内のコードは、次のように、提供された ReactiveTransaction
オブジェクトで setRollbackOnly()
メソッドを呼び出すことにより、トランザクションをロールバックできます。
Java
Kotlin
transactionalOperator.execute(new TransactionCallback<>() {
public Mono<Object> doInTransaction(ReactiveTransaction status) {
return updateOperation1().then(updateOperation2)
.doOnError(SomeBusinessException.class, e -> status.setRollbackOnly());
}
}
});
transactionalOperator.execute(object : TransactionCallback() {
override fun doInTransactionWithoutResult(status: ReactiveTransaction) {
updateOperation1().then(updateOperation2)
.doOnError(SomeBusinessException.class, e -> status.setRollbackOnly())
}
})
シグナルをキャンセル
Reactive Streams では、Subscriber
は Subscription
をキャンセルし、Publisher
を停止できます。プロジェクト Reactor および next()
、take(long)
、timeout(Duration)
などの他のライブラリのオペレーターは、キャンセルを発行できます。それがエラーによるものか、それ以上消費するだけの関心がないためか、キャンセルの理由を知る方法はありません。バージョン 5.3 以降のキャンセルシグナルはロールバックにつながります。その結果、トランザクション Publisher
のダウンストリームで使用される演算子を考慮することが重要です。特に Flux
または他の多値 Publisher
の場合、トランザクションを完了するには、出力全体を消費する必要があります。
トランザクション設定の指定
TransactionalOperator
のトランザクション設定 (伝播モード、分離レベル、タイムアウトなど) を指定できます。デフォルトでは、TransactionalOperator
インスタンスにはデフォルトのトランザクション設定があります。次の例は、特定の TransactionalOperator:
のトランザクション設定のカスタマイズを示しています。
Java
Kotlin
public class SimpleService implements Service {
private final TransactionalOperator transactionalOperator;
public SimpleService(ReactiveTransactionManager transactionManager) {
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
// the transaction settings can be set here explicitly if so desired
definition.setIsolationLevel(TransactionDefinition.ISOLATION_READ_UNCOMMITTED);
definition.setTimeout(30); // 30 seconds
// and so forth...
this.transactionalOperator = TransactionalOperator.create(transactionManager, definition);
}
}
class SimpleService(transactionManager: ReactiveTransactionManager) : Service {
private val definition = DefaultTransactionDefinition().apply {
// the transaction settings can be set here explicitly if so desired
isolationLevel = TransactionDefinition.ISOLATION_READ_UNCOMMITTED
timeout = 30 // 30 seconds
// and so forth...
}
private val transactionalOperator = TransactionalOperator(transactionManager, definition)
}
TransactionManager
を使用する
以下のセクションでは、命令型およびリアクティブ型トランザクションマネージャーのプログラムによる使用箇所について説明します。
PlatformTransactionManager
を使用する
命令型トランザクションの場合、org.springframework.transaction.PlatformTransactionManager
を直接使用してトランザクションを管理できます。そのためには、使用する PlatformTransactionManager
の実装を Bean 参照を通じて Bean に渡します。次に、TransactionDefinition
および TransactionStatus
オブジェクトを使用して、トランザクションを開始し、ロールバックしてコミットできます。次の例は、その方法を示しています。
Java
Kotlin
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// explicitly setting the transaction name is something that can be done only programmatically
def.setName("SomeTxName");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = txManager.getTransaction(def);
try {
// put your business logic here
} catch (MyException ex) {
txManager.rollback(status);
throw ex;
}
txManager.commit(status);
val def = DefaultTransactionDefinition()
// explicitly setting the transaction name is something that can be done only programmatically
def.setName("SomeTxName")
def.propagationBehavior = TransactionDefinition.PROPAGATION_REQUIRED
val status = txManager.getTransaction(def)
try {
// put your business logic here
} catch (ex: MyException) {
txManager.rollback(status)
throw ex
}
txManager.commit(status)
ReactiveTransactionManager
を使用する
リアクティブトランザクションを使用する場合、org.springframework.transaction.ReactiveTransactionManager
を直接使用してトランザクションを管理できます。そのためには、使用する ReactiveTransactionManager
の実装を Bean 参照を通じて Bean に渡します。次に、TransactionDefinition
および ReactiveTransaction
オブジェクトを使用して、トランザクションを開始し、ロールバックしてコミットできます。次の例は、その方法を示しています。
Java
Kotlin
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// explicitly setting the transaction name is something that can be done only programmatically
def.setName("SomeTxName");
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
Mono<ReactiveTransaction> reactiveTx = txManager.getReactiveTransaction(def);
reactiveTx.flatMap(status -> {
Mono<Object> tx = ...; // put your business logic here
return tx.then(txManager.commit(status))
.onErrorResume(ex -> txManager.rollback(status).then(Mono.error(ex)));
});
val def = DefaultTransactionDefinition()
// explicitly setting the transaction name is something that can be done only programmatically
def.setName("SomeTxName")
def.propagationBehavior = TransactionDefinition.PROPAGATION_REQUIRED
val reactiveTx = txManager.getReactiveTransaction(def)
reactiveTx.flatMap { status ->
val tx = ... // put your business logic here
tx.then(txManager.commit(status))
.onErrorResume { ex -> txManager.rollback(status).then(Mono.error(ex)) }
}