java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Spring事务管理

Spring事务管理之如何处理删除操作与事务回滚

作者:码农阿豪@新空间

在实际开发中,事务管理是保证数据一致性的核心机制之一,本文将通过一个实际案例,详细分析Spring事务中的删除操作与回滚机制,并提供优化方案,需要的可以参考下

引言

在实际开发中,事务管理是保证数据一致性的核心机制之一。特别是在涉及数据库删除操作时,如何正确处理删除失败、并发冲突等场景,是每个开发者需要面对的挑战。本文将通过一个实际案例,详细分析Spring事务中的删除操作与回滚机制,并提供优化方案。

1. 问题背景

在某个广告流量控制系统中,我们需要删除无效的媒体广告位放量记录。核心代码如下:

@Service
@Transactional
public class FlowTransactionalUtil {
    @Autowired
    private OpmMediaFlowControlConfirmService confirmService;

    public void deleteMediaFlowOnline(List<OpmMediaFlowControlEntity> list, long flowId) {
        List<Long> mediaAdList = list.stream()
                .map(OpmMediaFlowControlEntity::getMediaAdId)
                .collect(Collectors.toList());

        List<Long> deleteList = confirmService.list(QueryUtils.eq(OpmMediaFlowControlConfirmEntity::getFlowId, flowId))
                .stream()
                .filter(e -> !mediaAdList.contains(e.getMediaAdId()))
                .map(OpmMediaFlowControlConfirmEntity::getId)
                .collect(Collectors.toList());

        if (!CollectionUtils.isEmpty(deleteList)) {
            RollBackExcUtils.throwExc(confirmService.removeByIds(deleteList) ? 1 : 0);
        }
    }
}

问题现象:

当 removeByIds() 返回 false(删除失败或未删除记录)时,抛出 CustomerException 并回滚事务。

日志显示:

com.middle.exception.CustomerException: 事务处理请求异常

2. 问题分析

2.1 事务回滚机制

Spring 默认在遇到 未捕获的 RuntimeException 或 Error 时回滚事务。如果 CustomerException 不是 RuntimeException 的子类,需要显式声明@Transactional(rollbackFor = CustomerException.class)。

2.2 removeByIds 返回 false 的原因

原因是否应回滚处理建议
记录不存在记录日志,不抛异常
并发冲突(已删除)记录日志,不抛异常
数据库异常抛异常并回滚

3. 优化方案

3.1 区分业务异常与技术异常

优化 deleteFlowConfirm 方法,避免因“记录不存在”等合法场景触发回滚:

private void deleteFlowConfirm(long flowId, List<OpmMediaFlowControlConfirmEntity> controlConfirms) {
    List<Long> list = controlConfirms.stream()
            .filter(e -> e.getFlowId().equals(flowId))
            .map(OpmMediaFlowControlConfirmEntity::getId)
            .collect(Collectors.toList());

    if (!CollectionUtils.isEmpty(list)) {
        boolean isSuccess = confirmService.removeByIds(list);
        if (!isSuccess) {
            // 检查是否真的存在待删除记录
            long actualExistCount = confirmService.count(
                QueryUtils.in(OpmMediaFlowControlConfirmEntity::getId, list)
            );
            if (actualExistCount > 0) {
                throw new CustomerException("删除失败,请重试或联系管理员");
            } else {
                log.warn("尝试删除不存在的记录: flowId={}, ids={}", flowId, list);
            }
        }
    }
}

3.2 显式声明事务回滚规则

@Transactional(rollbackFor = {CustomerException.class, RuntimeException.class})
public class FlowService {
    // ...
}

3.3 并发场景优化

方案1:乐观锁重试

@Retryable(maxAttempts = 3, backoff = @Backoff(delay = 100))
public void deleteWithRetry(List<Long> ids) {
    TransactionAssert.assertSuccess(confirmService.removeByIds(ids));
}

方案2:悲观锁查询

List<OpmMediaFlowControlConfirmEntity> list = confirmService.listWithLock(
    QueryUtils.eq(OpmMediaFlowControlConfirmEntity::getFlowId, flowId)
);

4. 完整优化代码

@Service
@Transactional(rollbackFor = CustomerException.class)
public class FlowService {
    @Autowired
    private OpmMediaFlowControlConfirmService confirmService;

    public void deleteMediaFlowOnline(List<OpmMediaFlowControlEntity> list, long flowId) {
        List<Long> mediaAdList = list.stream()
                .map(OpmMediaFlowControlEntity::getMediaAdId)
                .collect(Collectors.toList());

        // 直接条件删除(避免查询-删除竞态条件)
        boolean success = confirmService.deleteByFlowIdAndExcludedAdIds(flowId, mediaAdList);
        TransactionAssert.assertSuccess(success);
    }
}

// 事务断言工具类
public class TransactionAssert {
    public static void assertSuccess(boolean condition) {
        if (!condition) {
            throw new CustomerException(SupResultCode.CODE_900000, "操作失败,请重试");
        }
    }
}

5. 日志与监控建议

在关键位置添加日志:

try {
    flowService.deleteMediaFlowOnline(list, flowId);
} catch (CustomerException e) {
    log.error("删除失败 - flowId: {}, 错误: {}", flowId, e.getMessage());
    throw e;
}

6. 总结

通过以上优化,可以显著提升系统的健壮性和可维护性。

到此这篇关于Spring事务管理之如何处理删除操作与事务回滚的文章就介绍到这了,更多相关Spring事务管理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文