程式化交易管理

Spring Framework 提供兩種程式化交易管理的方式,透過使用

  • TransactionTemplateTransactionalOperator

  • 直接使用 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。然後,透過使用 TransactionDefinitionTransactionStatus 物件,您可以啟動交易、回滾和提交。以下範例示範如何執行此操作

  • 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。然後,透過使用 TransactionDefinitionReactiveTransaction 物件,您可以啟動交易、回滾和提交。以下範例示範如何執行此操作

  • 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)) }
}