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

相关推荐
benpaodeDD2 小时前
黑马SpringBoot2自动配置原理
java·spring boot·后端
用户26851612107562 小时前
GMP 调度器深度学习笔记
后端·go
J_liaty2 小时前
SpringBoot深度解析i18n国际化:配置文件+数据库动态实现(简/繁/英)
spring boot·后端·i18n
牧小七2 小时前
springboot 配置访问上传图片
java·spring boot·后端
用户26851612107563 小时前
GMP 三大核心结构体字段详解
后端·go
一路向北⁢3 小时前
短信登录安全防护方案(Spring Boot)
spring boot·redis·后端·安全·sms·短信登录
古城小栈3 小时前
Tokio:Rust 异步界的 “霸主”
开发语言·后端·rust
进击的丸子3 小时前
基于虹软Linux Pro SDK的多路RTSP流并发接入、解码与帧级处理实践
java·后端·github
while(1){yan}3 小时前
SpringAOP
java·开发语言·spring boot·spring·aop