程式設計式事務管理
Spring Framework 提供了兩種程式設計式事務管理方式,分別是使用
-
TransactionTemplate
或TransactionalOperator
。 -
直接使用
TransactionManager
實現。
Spring 團隊通常建議在命令式流程中使用 TransactionTemplate
進行程式設計式事務管理,並在響應式程式碼中使用 TransactionalOperator
。第二種方法類似於使用 JTA UserTransaction
API,儘管異常處理沒那麼麻煩。
使用 TransactionTemplate
TransactionTemplate
採用了與其他 Spring 模板(如 JdbcTemplate
)相同的方法。它使用了回撥方式(將應用程式碼從事務資源的樣板式獲取和釋放中解放出來),並使程式碼意圖明確,因為您的程式碼只關注您想做什麼。
正如以下示例所示,使用 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
,您需要建立兩個不同的 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
可以透過兩種方式使用
-
運算子風格,使用 Project 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
。Project 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
來管理事務。為此,透過 bean 引用將您使用的 PlatformTransactionManager
實現傳遞給您的 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
來管理事務。為此,透過 bean 引用將您使用的 ReactiveTransactionManager
實現傳遞給您的 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)) }
}