You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java执行方法时设DB状态为in progress,异常时回滚状态的问题

问题分析

当前代码的核心问题在于使用独立线程更新"in progress"状态:

  • 独立线程的事务会单独提交,当主线程发生异常时,该状态已经持久化到数据库,无法回滚。
  • getOne返回的是实体代理,若主线程事务结束后线程才执行,可能导致代理脱离上下文,引发保存失败。
  • 未对"some code..."部分的异常进行捕获,一旦抛出异常,状态会停留在"in progress"。
解决方案

1. 移除独立线程,统一事务/分事务处理

不要用独立线程更新状态,改为在主线程中处理。若需要"in progress"状态立即对其他请求可见,可使用独立事务更新该状态;若不需要即时可见,可放在同一事务中(但此时状态不会在部署过程中显示为"in progress")。

2. 捕获原始状态,异常时回滚

在更新为"in progress"前记录原始状态,一旦发生异常,将状态恢复为原始值。

修复后代码示例

假设使用Spring框架,我们可以通过@Transactional(propagation = Propagation.REQUIRES_NEW)创建独立事务更新状态:

第一步:创建状态更新的服务方法

@Service
public class DeploymentService {
    private final ClientConfigRepository clientConfigRepository;

    public DeploymentService(ClientConfigRepository clientConfigRepository) {
        this.clientConfigRepository = clientConfigRepository;
    }

    // 独立事务更新状态,确保即时可见
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void updateDeploymentStatus(Long clientId, String status) {
        ClientConfig clientConfig = clientConfigRepository.findById(clientId)
                .orElseThrow(() -> new IllegalArgumentException("Client config not found for id: " + clientId));
        clientConfig.setDeploymentStatus(status);
        clientConfigRepository.save(clientConfig);
    }

    // 独立事务更新成功状态及时间
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void markDeploymentSuccess(Long clientId) {
        ClientConfig clientConfig = clientConfigRepository.findById(clientId)
                .orElseThrow(() -> new IllegalArgumentException("Client config not found for id: " + clientId));
        clientConfig.setDeploymentStatus("deployed");
        clientConfig.setLastDeployedDate(new Date());
        clientConfigRepository.save(clientConfig);
    }
}

第二步:重构主业务方法

@RestController
public class DeploymentController {
    private final ClientConfigRepository clientConfigRepository;
    private final DeploymentService deploymentService;
    private final RestResponse restResponse;

    public DeploymentController(ClientConfigRepository clientConfigRepository,
                                DeploymentService deploymentService,
                                RestResponse restResponse) {
        this.clientConfigRepository = clientConfigRepository;
        this.deploymentService = deploymentService;
        this.restResponse = restResponse;
    }

    public ResponseEntity<?> deployConfigs(Long clientId) {
        // 获取原始状态
        ClientConfig originalConfig = clientConfigRepository.findById(clientId)
                .orElseThrow(() -> new IllegalArgumentException("Client config not found for id: " + clientId));
        String originalStatus = originalConfig.getDeploymentStatus();

        try {
            // 设置为in progress(独立事务,即时生效)
            deploymentService.updateDeploymentStatus(clientId, "in progress");

            // 执行核心部署逻辑
            // some code....
            // some code...

            // 部署成功,更新状态
            deploymentService.markDeploymentSuccess(clientId);

            return new ResponseEntity<>(restResponse.createSuccessIdResponse(
                    "Successfully completed Deployment of Configs.", clientId, ""), HttpStatus.OK);
        } catch (Exception e) {
            // 异常时回滚到原始状态
            deploymentService.updateDeploymentStatus(clientId, originalStatus);
            return new ResponseEntity<>(restResponse.commonErrorResponse(
                    "Deploy Config implementation failed", e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR.toString()), HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
}

额外注意事项

  • 如果部署逻辑耗时较长,可考虑添加超时机制或定时任务清理停滞的"in progress"状态(比如超过一定时间未更新为"deployed"则自动回滚)。
  • 避免使用getOne,改用findById获取实体,防止代理脱离上下文导致的异常。
  • 确保所有可能抛出异常的代码都被try-catch覆盖,包括第三方服务调用、数据库操作等。

内容的提问来源于stack exchange,提问作者bikash pradhan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 04:10:37