springboot怎么将事务设置为pending,等另外一个请求ok了,再做commit

在Spring Boot中实现跨请求的事务管理是一个复杂的需求,因为HTTP协议是无状态的,而数据库事务通常绑定在一个请求的上下文中。不过,有几种方案可以实现类似的效果:

方案1:使用编程式事务 + 状态存储(推荐)

1.1 手动控制事务边界

复制代码
@Service
public class DistributedTransactionService {
    @Autowired
    private PlatformTransactionManager transactionManager;
    
    @Autowired
    private TransactionTemplate transactionTemplate;
    
    // 存储事务状态
    private Map<String, TransactionStatus> transactionStore = new ConcurrentHashMap<>();
    
    /**
     * 第一阶段:开始事务并保存状态
     */
    public String startPendingTransaction() {
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        
        TransactionStatus status = transactionManager.getTransaction(def);
        String transactionId = UUID.randomUUID().toString();
        transactionStore.put(transactionId, status);
        
        // 执行业务操作
        // yourBusinessLogic();
        
        return transactionId;
    }
    
    /**
     * 第二阶段:提交事务
     */
    public void commitTransaction(String transactionId) {
        TransactionStatus status = transactionStore.get(transactionId);
        if (status != null) {
            try {
                // 执行业务验证
                if (validateBusiness()) {
                    transactionManager.commit(status);
                } else {
                    transactionManager.rollback(status);
                }
            } finally {
                transactionStore.remove(transactionId);
            }
        }
    }
    
    /**
     * 回滚事务
     */
    public void rollbackTransaction(String transactionId) {
        TransactionStatus status = transactionStore.get(transactionId);
        if (status != null) {
            transactionManager.rollback(status);
            transactionStore.remove(transactionId);
        }
    }
}

方案2:使用Saga模式(分布式事务)

2.1 添加依赖

复制代码
<dependency>
    <groupId>io.eventuate.tram.sagas</groupId>
    <artifactId>eventuate-tram-sagas-spring-orchestration-simple-dsl</artifactId>
    <version>0.21.0.RELEASE</version>
</dependency>

2.2 实现Saga协调器

复制代码
public class TwoPhaseTransactionSaga implements SimpleSaga<TwoPhaseTransactionData> {
    
    @Autowired
    private TransactionService transactionService;
    
    @Override
    public SagaDefinition<SimpleSagaDsl<TwoPhaseTransactionData>, TwoPhaseTransactionData> getSagaDefinition() {
        return Saga
            .step()
                .invokeLocal(this::beginTransaction)
                .withCompensation(this::rollbackTransaction)
            .step()
                .invokeLocal(this::validateAndCommit)
            .build();
    }
    
    private void beginTransaction(TwoPhaseTransactionData data) {
        // 开始事务,但不提交
        transactionService.beginPendingTransaction(data.getTransactionId());
    }
    
    private void validateAndCommit(TwoPhaseTransactionData data) {
        // 等待其他请求验证
        boolean isValid = waitForValidation(data.getTransactionId());
        if (isValid) {
            transactionService.commitTransaction(data.getTransactionId());
        } else {
            throw new RuntimeException("Validation failed");
        }
    }
    
    private void rollbackTransaction(TwoPhaseTransactionData data) {
        transactionService.rollbackTransaction(data.getTransactionId());
    }
}

方案3:使用数据库SAVEPOINT(适用于同一连接)

复制代码
@Service
public class SavepointTransactionService {
    
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    @Autowired
    private DataSource dataSource;
    
    public void executeWithPendingCommit() {
        Connection conn = null;
        Savepoint savepoint = null;
        
        try {
            conn = DataSourceUtils.getConnection(dataSource);
            conn.setAutoCommit(false);  // 关闭自动提交
            
            // 创建保存点
            savepoint = conn.setSavepoint("pending_transaction");
            
            // 执行第一阶段操作
            jdbcTemplate.update("INSERT INTO table1 ...");
            jdbcTemplate.update("UPDATE table2 ...");
            
            // 此时事务未提交,等待其他请求
            
            // 在另一个请求中验证并提交
            // validateAndCommit(conn, savepoint);
            
        } catch (Exception e) {
            if (savepoint != null) {
                conn.rollback(savepoint);
            }
            throw new RuntimeException(e);
        }
    }
    
    /**
     * 在另一个请求中调用
     */
    public void validateAndCommit(Connection conn, Savepoint savepoint) {
        try {
            // 执行业务验证
            boolean isValid = validateBusinessLogic();
            
            if (isValid) {
                conn.commit();  // 提交事务
            } else {
                conn.rollback(savepoint);  // 回滚到保存点
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            if (conn != null) {
                try {
                    conn.setAutoCommit(true);
                    conn.close();
                } catch (SQLException e) {
                    // 处理异常
                }
            }
        }
    }
}

方案4:使用消息队列实现最终一致性

复制代码
@Service
public class MessageBasedTransactionService {
    
    @Autowired
    private JmsTemplate jmsTemplate;
    
    @Autowired
    private TransactionalEventPublisher eventPublisher;
    
    /**
     * 第一阶段:发布事件但不提交
     */
    @Transactional
    public void phaseOne(String businessData) {
        // 保存业务数据
        businessRepository.save(businessData);
        
        // 发布事件(在事务提交后才发送)
        eventPublisher.publishEvent(new TransactionPendingEvent(businessData));
    }
    
    /**
     * 监听事件,执行第二阶段
     */
    @JmsListener(destination = "transaction.pending")
    public void handlePendingTransaction(TransactionPendingEvent event) {
        // 执行业务验证
        boolean isValid = validateBusiness(event.getData());
        
        if (isValid) {
            // 执行提交操作
            commitTransaction(event.getTransactionId());
        } else {
            // 执行回滚操作
            rollbackTransaction(event.getTransactionId());
        }
    }
}

方案5:使用状态机管理事务状态

复制代码
@Service
public class StateMachineTransactionService {
    
    public enum TransactionState {
        PENDING, VALIDATING, COMMITTED, ROLLBACKED
    }
    
    public enum TransactionEvent {
        VALIDATE, COMMIT, ROLLBACK
    }
    
    @Transactional
    public String startTransactionWithStateMachine() {
        String transactionId = UUID.randomUUID().toString();
        
        // 创建状态机
        StateMachine<TransactionState, TransactionEvent> stateMachine = 
            stateMachineFactory.getStateMachine(transactionId);
        
        // 初始状态为PENDING
        stateMachine.sendEvent(TransactionEvent.VALIDATE);
        
        // 执行业务操作
        executeBusinessLogic();
        
        // 返回事务ID供后续查询
        return transactionId;
    }
    
    @Transactional
    public void commitTransaction(String transactionId) {
        StateMachine<TransactionState, TransactionEvent> stateMachine = 
            stateMachineFactory.getStateMachine(transactionId);
        
        if (stateMachine.getState().getId() == TransactionState.VALIDATING) {
            stateMachine.sendEvent(TransactionEvent.COMMIT);
            // 执行实际的提交逻辑
            executeCommit();
        }
    }
}

最佳实践建议

  1. 考虑使用成熟的分布式事务框架

    • Seata

    • Atomikos

    • Narayana

  2. 设计注意事项

    复制代码
    // 1. 设置超时机制
    @Transactional(timeout = 300)  // 5分钟超时
    
    // 2. 使用异步处理
    @Async
    public CompletableFuture<String> processAsync() {
        // 异步处理
    }
    
    // 3. 添加幂等性处理
    public void processWithIdempotent(String idempotentKey) {
        if (processedKeys.contains(idempotentKey)) {
            return;  // 已处理过
        }
        // 执行业务逻辑
        processedKeys.add(idempotentKey);
    }
  3. 清理机制

    @Component
    public class TransactionCleanupScheduler {

    复制代码
     @Scheduled(fixedDelay = 60000)  // 每分钟清理一次
     public void cleanupPendingTransactions() {
         // 清理超时的事务
         transactionStore.entrySet().removeIf(entry -> 
             isTransactionTimeout(entry.getValue())
         );
     }

    }

注意事项

  1. 事务隔离级别:长时间挂起的事务可能导致锁竞争

  2. 连接池限制:挂起的事务会占用数据库连接

  3. 超时处理:需要设置合理的超时时间

  4. 异常处理:确保异常情况下能正确清理资源

  5. 分布式环境:在集群环境下需要共享事务状态

根据你的具体场景选择最合适的方案,对于简单的需求,方案1(编程式事务)通常足够;对于复杂的分布式场景,建议使用Saga模式或成熟的分布式事务框架。

相关推荐
leonkay1 天前
Golang语言闭包完全指南
开发语言·数据结构·后端·算法·架构·golang
颜酱1 天前
BFS 与并查集实战总结:从基础框架到刷题落地
javascript·后端·算法
无限大61 天前
数字生存02:如何在信息爆炸的时代保持清醒,不被算法控制
后端
无限大61 天前
AI实战02:一个万能提示词模板,搞定90%的文案/设计/分析需求
前端·后端
青柠代码录1 天前
【Linux】脚本:console.log 日志定期备份清理
后端
菜鸟‍1 天前
【后端项目】苍穹外卖day01-开发环境搭建
java·开发语言·spring boot
陈随易1 天前
站在普通开发者的角度,我觉得 RollCode 更像是“把 H5 交付这件事重新捋顺了”
前端·后端·程序员
陈随易1 天前
RollCode:不只是在做页面,而是在缩短“从需求到上线”的整条链路
前端·后端
y = xⁿ1 天前
【LeetCodehot100】二叉树大合集 T94:二叉树的中序遍历 T104:二叉树的最大深度 T226:翻转二叉树 T101:对称二叉树
后端·算法·深度优先
2501_921649491 天前
美股历史 K线数据 API接口综合评测与接入指南
后端·python·websocket·金融·restful