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

相关推荐
Victory_orsh几秒前
AI雇佣人类,智能奴役肉体
后端
金牌归来发现妻女流落街头10 分钟前
【Springboot基础开发】
java·spring boot·后端
李日灐43 分钟前
C++进阶必备:红黑树从 0 到 1: 手撕底层,带你搞懂平衡二叉树的平衡逻辑与黑高检验
开发语言·数据结构·c++·后端·面试·红黑树·自平衡二叉搜索树
是梦终空1 小时前
计算机毕业设计264—基于Springboot+Vue3+协同过滤的房屋租赁管理系统(源代码+数据库+万字论文+设计文档)
spring boot·毕业设计·vue3·课程设计·毕业论文·协同过滤·房屋租赁管理系统
qq_297574671 小时前
【实战】POI 实现 Excel 多级表头导出(含合并单元格完整方案)
java·spring boot·后端·excel
郝学胜-神的一滴1 小时前
超越Spring的Summer(一): PackageScanner 类实现原理详解
java·服务器·开发语言·后端·spring·软件构建
Tony Bai2 小时前
“Go 2,请不要发生!”:如果 Go 变成了“缝合怪”,你还会爱它吗?
开发语言·后端·golang
Victor3562 小时前
Hibernate(91)如何在数据库回归测试中使用Hibernate?
后端
Victor3562 小时前
MongoDB(1)什么是MongoDB?
后端
Victor3568 小时前
https://editor.csdn.net/md/?articleId=139321571&spm=1011.2415.3001.9698
后端