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

相关推荐
阳光是sunny7 小时前
LangGraph中的Reducer是什么
前端·人工智能·后端
灯澜忆梦7 小时前
GO_并发编程---定时器
开发语言·后端·golang
阳光是sunny7 小时前
从链到图:LangGraph 入门基础全解析
前端·人工智能·后端
鱟鲥鳚7 小时前
Spring Boot 集成 LangChain4j:从模型调用到 Tool Calling(Demo版)
java·spring boot
皮皮林5517 小时前
开源实力派,给本地 AI 装上深度调研能力,一张 3090 跑到 95.7 分!
后端
努力的小雨8 小时前
把 Windows 桌面图标做成一个会自己运转的小世界
后端
武子康8 小时前
Search Console Platform Properties 扩大 SEO 资产边界:从 Page Ranking 到 Topic Coverage(5 类误读边界 + 3 表数据层设计)
前端·人工智能·后端
腾渊信息科技公司9 小时前
Spring Boot对接MES实战:视觉检测数据自动同步方案
java·人工智能·spring boot·后端·计算机视觉·ai·软件需求
IT_陈寒10 小时前
Vue这个特性差点让我加班到凌晨,谁懂啊
前端·人工智能·后端
段一凡-华北理工大学10 小时前
向量数据库实战:选型、调优与落地~系列文章12:文本分块策略实战:chunk_size 怎么选?重叠多少?
开发语言·数据库·后端·oracle·rust·工业智能体·高炉智能化