在java jdbc中实现saga模式,核心在于将分布式事务拆分为一系列本地事务,并通过补偿逻辑保证最终一致性。具体做法是维护一个操作栈,正向执行时记录每个步骤的id和对应的补偿函数(compensating action),一旦某步失败,立即按逆序调用已执行步骤的补偿方法回滚状态。

JDBC事务中的正向与反向操作如何定义
正向操作是指创建订单、扣减库存等改变业务状态的动作,而反向操作则是取消订单、恢复库存等用于撤销前一步动作的逻辑。在JDBC环境下,每个操作都包裹在一个独立的数据库事务中,通过`Connection.setAutoCommit(false)`开启,并在成功或失败后显式提交或回滚。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayDeque;
import java.util.Deque;
public class SagaOrchestrator {
private Deque<Runnable> compensationStack = new ArrayDeque<>();
public void executeOrder(String orderId, String productId) throws Exception {
Connection conn = null;
try {
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db", "user", "pass");
conn.setAutoCommit(false);
// 1. Create Order
createOrder(conn, orderId);
compensationStack.push(() -> cancelOrder(conn, orderId));
// 2. Deduct Inventory
deductInventory(conn, productId);
compensationStack.push(() -> restoreInventory(conn, productId));
conn.commit();
} catch (Exception e) {
if (conn != null) conn.rollback();
compensate(e);
throw e;
}
}
private void compensate(Exception lastError) {
while (!compensationStack.isEmpty()) {
Runnable comp = compensationStack.pop();
try {
comp.run();
} catch (Exception ex) {
// Log compensation failure for manual intervention
System.err.println("Compensation failed: " + ex.getMessage());
}
}
}
private void createOrder(Connection conn, String orderId) throws SQLException {
// SQL: INSERT INTO orders ... VALUES (?, 'CREATED')
}
private void cancelOrder(Connection conn, String orderId) throws SQLException {
// SQL: UPDATE orders SET status = 'CANCELLED' WHERE id = ?
}
private void deductInventory(Connection conn, String productId) throws SQLException {
// SQL: UPDATE inventory SET count = count - 1 WHERE product_id = ?
}
private void restoreInventory(Connection conn, String productId) throws SQLException {
// SQL: UPDATE inventory SET count = count + 1 WHERE product_id = ?
}
}
上述代码展示了最基础的Saga编排器结构。`createOrder`和`deductInventory`是正向操作,它们被压入`compensationStack`之前,必须确保其对应的补偿逻辑(如`cancelOrder`)已经准备好。如果`deductInventory`抛出异常,程序会进入`catch`块,触发`compensate`方法,此时栈顶是`restoreInventory`,接着是`cancelOrder`,从而实现了逆向回滚。
立即学习“Java免费学习笔记(深入)”;
你是 TypeScript 项目架构专家,专注于脚手架搭建生产就绪的 Node.js 和前端应用。生成包含现代工具链(pnpm、Vite、Next.js)、类型
JDBC连接管理在Saga流程中需要注意什么
在Saga模式中,由于涉及多个独立的事务步骤,JDBC连接的复用和生命周期管理至关重要。通常建议为每个Saga实例分配独立的连接,或者使用线程安全的连接池获取新连接,避免不同Saga步骤之间因共享连接导致的状态污染或死锁。

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayDeque;
import java.util.Deque;
public class RobustSagaOrchestrator {
private Deque<Step> steps = new ArrayDeque<>();
public void process(String orderId, String productId) {
Connection orderConn = null;
Connection invConn = null;
try {
orderConn = getNewConnection();
invConn = getNewConnection();
// Step 1: Order Service
orderConn.setAutoCommit(false);
insertOrder(orderConn, orderId);
steps.push(new Step("CancelOrder", () -> cancelOrder(orderConn, orderId)));
orderConn.commit();
// Step 2: Inventory Service
invConn.setAutoCommit(false);
updateInventory(invConn, productId);
steps.push(new Step("RestoreInventory", () -> restoreInventory(invConn, productId)));
invConn.commit();
} catch (SQLException e) {
rollbackSteps(steps);
} finally {
closeQuietly(orderConn);
closeQuietly(invConn);
}
}
private void rollbackSteps(Deque<Step> s) {
while (!s.isEmpty()) {
Step step = s.pop();
try {
step.compensate();
} catch (Exception e) {
System.err.println("Compensation error: " + e.getMessage());
}
}
}
private Connection getNewConnection() throws SQLException {
return DriverManager.getConnection("jdbc:mysql://localhost:3306/db", "user", "pass");
}
private void closeQuietly(Connection conn) {
if (conn != null) {
try { conn.close(); } catch (SQLException ignored) {}
}
}
static class Step {
String name;
Runnable action;
Step(String n, Runnable a) { name = n; action = a; }
void compensate() throws Exception { action.run(); }
}
private void insertOrder(Connection c, String id) throws SQLException {}
private void cancelOrder(Connection c, String id) throws SQLException {}
private void updateInventory(Connection c, String pid) throws SQLException {}
private void restoreInventory(Connection c, String pid) throws SQLException {}
}
在实际开发中,隔离不同服务的数据库连接可以避免跨库事务的复杂性。例如,订单服务使用`orderConn`,库存服务使用`invConn`。当第二步失败时,第一步的事务已经提交,因此不能通过简单的`rollback`撤销,必须依赖`steps`队列中存储的补偿动作来手动修正数据状态。这种设计确保了即使某个步骤失败,之前的操作也能通过补偿逻辑达到一致状态。
补偿操作失败时如何处理
当补偿操作本身也失败时,系统无法自动恢复到一致状态,这被称为“不可补偿”场景。此时需要引入人工干预机制或持久化补偿日志,将失败的Saga实例标记为“需要人工处理”,并记录详细的错误信息以便后续排查和修复。

















