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模式或成熟的分布式事务框架。

相关推荐
一点程序1 天前
基于SpringBoot的选课调查系统
java·spring boot·后端·选课调查系统
奋进的芋圆1 天前
Spring Boot 实现三模安全登录:微信扫码 + 手机号验证码 + 邮箱验证码
spring boot·redis·微信
怪兽源码1 天前
基于SpringBoot的选课调查系统
java·spring boot·后端·选课调查系统
csdn_aspnet1 天前
ASP.NET Core 中的依赖注入
后端·asp.net·di·.net core
昊坤说不出的梦1 天前
【实战】监控上下文切换及其优化方案
java·后端
疯狂踩坑人1 天前
【Python版 2026 从零学Langchain 1.x】(二)结构化输出和工具调用
后端·python·langchain
m0_740043731 天前
【无标题】
java·spring boot·spring·spring cloud·微服务
重整旗鼓~1 天前
1.外卖项目介绍
spring boot
橘子师兄1 天前
C++AI大模型接入SDK—ChatSDK封装
开发语言·c++·人工智能·后端
@ chen1 天前
Spring事务 核心知识
java·后端·spring