在实际开发中,事务管理是保证数据一致性的核心机制之一。特别是在涉及数据库删除操作时,如何正确处理删除失败、并发冲突等场景,是每个开发者需要面对的挑战。本文将通过一个实际案例,详细分析Spring事务中的删除操作与回滚机制,并提供优化方案。
在某个广告流量控制系统中,我们需要删除无效的媒体广告位放量记录。核心代码如下:
@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: 事务处理请求异常Spring 默认在遇到 未捕获的 RuntimeException 或 Error 时回滚事务。如果 CustomerException 不是 RuntimeException 的子类,需要显式声明 @Transactional(rollbackFor = CustomerException.class)。
removeByIds 返回 false 的原因原因 | 是否应回滚 | 处理建议 |
|---|---|---|
记录不存在 | 否 | 记录日志,不抛异常 |
并发冲突(已删除) | 否 | 记录日志,不抛异常 |
数据库异常 | 是 | 抛异常并回滚 |
优化 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);
}
}
}
}@Transactional(rollbackFor = {CustomerException.class, RuntimeException.class})
public class FlowService {
// ...
}@Retryable(maxAttempts = 3, backoff = @Backoff(delay = 100))
public void deleteWithRetry(List<Long> ids) {
TransactionAssert.assertSuccess(confirmService.removeByIds(ids));
}List<OpmMediaFlowControlConfirmEntity> list = confirmService.listWithLock(
QueryUtils.eq(OpmMediaFlowControlConfirmEntity::getFlowId, flowId)
);@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, "操作失败,请重试");
}
}
}在关键位置添加日志:
try {
flowService.deleteMediaFlowOnline(list, flowId);
} catch (CustomerException e) {
log.error("删除失败 - flowId: {}, 错误: {}", flowId, e.getMessage());
throw e;
}通过以上优化,可以显著提升系统的健壮性和可维护性。