if-else中手动调用rollback()不触发spring事务回滚,因未抛出runtimeexception;应统一用异常中断流程,并确保补偿逻辑幂等可重入。

if-else 里直接写 rollback() 会破坏事务边界
Spring 的 @Transactional 默认只对未捕获的 RuntimeException 及其子类触发回滚。如果你在 if-else 分支里手动调用 rollback(),但没抛出异常,事务管理器根本不会感知——数据库操作照常提交。
- 错误写法:
if (balance → <code>rollback()是无效调用,Spring 不认 - 正确做法:必须抛出异常,让 Spring 拦截并回滚,比如
throw new InsufficientBalanceException() - 注意
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()虽然能强制标记回滚,但属于“补救式”操作,掩盖了业务语义,且容易漏掉后续逻辑
多条件校验失败时,不同 if-else 分支应抛不同自定义异常
同一个方法里有多个前置校验(如用户状态、库存、权限),每个失败原因需对应独立异常类型,否则统一异常处理器无法差异化响应。
- 不要全用
throw new ServiceException("校验失败")—— 前端无法区分是“账号被冻结”还是“商品已下架” - 应定义语义明确的异常类:
UserDisabledException、ProductOutOfStockException、PermissionDeniedException - 统一异常处理器中,为每种异常映射不同 HTTP 状态码和错误码:
UserDisabledException → 403, "USER_DISABLED" - 避免在 if-else 里做 try-catch 包裹异常再转译,这会让错误堆栈变深、丢失原始位置
if-else 控制回滚时机时,注意嵌套层级与空指针风险
当校验依赖外部查询结果(如查订单、查用户),if-else 容易写出 if (order == null) 这类判空逻辑,但若 order 查询本身抛了 DB 异常,整个事务可能已处于不一致状态。
- 推荐用
Optional链式处理:orderRepository.findById(id).orElseThrow(() -> new OrderNotFoundException(id)) - if-else 中避免重复查库:先查一次,把结果存在局部变量,再用 if-else 判断它的字段,而不是每个分支都查一遍
- 特别警惕
if (user.getRole() != null && user.getRole().equals("ADMIN"))—— 若user为 null,直接 NPE,事务还没走到回滚就崩了
回滚不是终点,if-else 分支里要确保补偿动作可重入
有些场景不能只靠数据库事务回滚(比如已调用第三方支付接口、发了 MQ 消息),这时 if-else 分支里得主动执行补偿逻辑,而补偿代码必须支持重复执行不翻车。
- 例如:if 支付成功但本地订单落库失败 → 进入 else 分支,调用支付平台“查单+退单”,该退单接口必须幂等
- 不要在 if-else 里写
sendEmail("下单失败通知")而不加去重 ID,重试时可能发两封 - 关键点:补偿动作的触发条件要和主流程严格对齐,比如用同一张订单表的
status字段值作为判断依据,而不是靠时间戳或随机标志位










