JDK8 并行处理 事务全部回滚

/**
 * 首先 并行处理的数据必须保存在一个事务之内,会基于Spring进行管理
 */
@Transactional
public Long save(Request request, Long masterId) { 
        // 1. 保存/更新主表
        Long id = tbDomainService.saveOrUpdateMain(mainEntity);
        // 2. 批量保存所有明细表
        batchSaveAllDetails(request,id);
}
public void batchSaveAllDetails(Request request,Long id) {
    // 失败信号量:原子类保证线程安全,标记是否有任务失败
    AtomicBoolean hasFailed = new AtomicBoolean(false);
    // 存储异常信息:捕获第一个失败的异常(避免后续异常覆盖)
    AtomicReference<Throwable> failureException = new AtomicReference<>();
    List<CompletableFuture<Void>> futures = new ArrayList<>();        
    futures.add(CompletableFuture.runAsync(() -> {
        if (hasFailed.get()) return;
        try {
            batchPlanDetails(request, id);
        } catch (Throwable t) {
            handleFailure(hasFailed, failureException, t);
        }
    }));
    futures.add(CompletableFuture.runAsync(() -> {
        if (hasFailed.get()) return;
        try {
            batchDetails(request, id);
        } catch (Throwable t) {
            handleFailure(hasFailed, failureException, t);
        }
    }));
    // 等待所有无依赖任务完成(无论成功/失败)
    CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
    //  检查是否有任务失败,有则抛出异常触发事务回滚
    if (hasFailed.get()) {
        Throwable cause = failureException.get();
        if (cause instanceof UdpNotifyAppException) {
            throw (UdpNotifyAppException) cause;
        } else {
            throw new UdpNotifyAppException("明细表处理失败", cause);
        }
    }
}
/**
 * 处理任务失败:标记信号量+存储异常(确保只记录第一个失败的异常)
 */
private void handleFailure(AtomicBoolean hasFailed, AtomicReference<Throwable> failureException, Throwable t) {
    // compareAndSet:保证只有第一个失败的任务会设置异常(避免后续异常覆盖)
    if (hasFailed.compareAndSet(false, true)) {
        failureException.set(t);
    }
    // 抛出异常,让 CompletableFuture 标记为失败状态(不影响信号量逻辑)
    if (t instanceof RuntimeException) {
        throw (RuntimeException) t;
    } else {
        throw new RuntimeException(t);
    }
}

// 具体的明细处理内容
private void batchPlanDetails(Request request,Long id) {

}

private void batchDetails(Request request,Long id) {

}
  1. 所有 CompletableFuture.runAsync() 虽然在不同线程执行,但 Spring 的事务是通过 ThreadLocal 管理的

  2. 在同一个事务方法内,即使跨线程,数据库操作仍然使用同一个数据库连接

  3. 任何线程的异常都会传播到主线程,触发事务回滚

  4. AtomicBoolean 让其他任务快速跳过

  5. 确保所有异步任务都使用相同的数据库连接池配置,避免连接泄露。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值