當應用程序的表示層需要協調多個 API 層方法調用以完成單個邏輯工作單元(LUW)時,客戶端編排事務策略應運而生。本文詳細闡述該策略的架構、實現規則、適用場景與限制,並提供基於 EJB 3.0 的完整代碼示例。
若 API 層設計為細粒度(chatty),客戶端需發起多次調用才能完成一個業務單元,且難以重構為粗粒度聚合 API,則客戶端編排策略是正確選擇。此策略將事務邊界前移至客戶端層,由客戶端控制事務的開始、提交與回滾。

MANDATORY 傳播屬性,要求調用者提供事務上下文,不包含回滾邏輯。MANDATORY,拒絕無事務上下文的調用。SUPPORTS,若在事務中調用則加入事務,否則無事務運行。// 客戶端層
public void getTrade() {
InitialContext ctx = new InitialContext();
TradingService service = (TradingService) ctx.lookup("TradingServiceImpl/remote");
TradeData trade = service.getTrade(11); // 無事務邏輯
}
// API 層
@TransactionAttribute(TransactionAttributeType.SUPPORTS)
public TradeData getTrade(long tradeId) {
return em.find(TradeData.class, tradeId);
}
// 客戶端層 - 事務擁有者
public void placeTrade() {
UserTransaction txn = null;
try {
InitialContext ctx = new InitialContext();
TradingService service = (TradingService) ctx.lookup("TradingServiceImpl/remote");
TradeData trade = new TradeData();
// 設置交易數據...
txn = (UserTransaction) ctx.lookup("UserTransaction");
txn.begin(); // 1. 開始事務
service.insertTrade(trade); // 2. 調用 API
service.updateAcct(trade);
txn.commit(); // 3. 提交事務
} catch (Exception e) {
if (txn != null) txn.rollback(); // 4. 異常回滾
throw e;
}
}
// API 層 - 無回滾邏輯,強制要求事務上下文
@TransactionAttribute(TransactionAttributeType.MANDATORY)
public TradeData insertTrade(TradeData trade) {
trade.setStage("PLACED");
em.persist(trade);
return trade;
}
@TransactionAttribute(TransactionAttributeType.MANDATORY)
public void updateAcct(TradeData trade) {
AcctData acct = em.find(AcctData.class, trade.getAcctId());
// 更新餘額邏輯...
}
MANDATORY,若客戶端未啟動事務直接調用將拋異常;混合使用 API 層策略會導致數據不一致。若必須支持非事務客戶端,可引入「代理 API 層」:該層使用 API 層策略(單一調用、REQUIRED),內部調用客戶端編排策略的 API 層。代理層成為事務擁有者,屏蔽底層複雜性。
客戶端編排策略適用於細粒度 API、需多調用完成 LUW 的場景,但對架構施加較強限制。選型前需權衡:重構為粗粒度 API(使用 API 層策略) vs 接受協議限制與開發複雜度。切勿嘗試在同一應用混合多種策略。