JPA异常需按语义映射HTTP状态码:EntityNotFoundException→404,DataIntegrityViolationException→400/409,OptimisticLockException→409,兜底异常→500;Spring中用@ControllerAdvice统一处理,JAX-RS中用@Provider实现ExceptionMapper。

Java中JPA操作抛出的非受检异常(如PersistenceException及其子类EntityNotFoundException、OptimisticLockException、DataIntegrityViolationException等)本身不带HTTP语义,不能直接映射为状态码。准确映射的关键在于:**区分异常语义、统一拦截入口、按业务上下文决策状态码,而非机械绑定异常类型**。
识别核心异常类型与对应语义
不要把所有JPA异常都转成500。需结合Spring Data JPA或Hibernate实际抛出行为判断真实含义:
-
EntityNotFoundException:明确表示“查不到资源”,应映射为404 NOT_FOUND(不是500) -
DataIntegrityViolationException:常见于唯一键冲突、外键约束失败,属客户端数据问题,应映射为400 BAD_REQUEST或更细粒度的409 CONFLICT(如重复注册) -
OptimisticLockException:并发修改冲突,属预期内业务场景,建议返回409 CONFLICT并附带重试提示 -
PersistenceException/TransactionSystemException:兜底异常,无明确业务含义,才归为500 INTERNAL_SERVER_ERROR
在Spring中统一拦截并映射
使用@ControllerAdvice + @ExceptionHandler是最常用且可控的方式。避免在每个Service里手动try-catch:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 捕获具体异常子类,优先于父类(如先写
@ExceptionHandler(EntityNotFoundException.class),再写@ExceptionHandler(PersistenceException.class)) - 返回
ResponseEntity,显式设置状态码和标准化错误体(含code、message、traceId),不依赖response.status !== 200做前端判断 - 对敏感信息脱敏:不输出SQL、表名、堆栈路径;
message用预设文案,例如"请求的数据不存在"而非e.getMessage()
避免状态码误用的典型陷阱
很多团队踩坑源于混淆协议层与业务层语义:
立即学习“Java免费学习笔记(深入)”;
- 不要因数据库主键为空就返回
400——这可能是前端漏传ID,也可能是后端逻辑缺陷,需结合参数校验(@Valid)分清责任边界 -
404只用于“资源不存在”场景;若ID格式非法(如传了字符串"abc"),应由参数解析阶段抛MethodArgumentTypeMismatchException,映射为400 - 事务回滚后仍返回
200是合理设计(RESTful强调“操作成功送达”,不代表业务成功),此时必须靠响应体中的success: false或code字段告知前端失败
补充:JAX-RS环境下的等效做法
若用的是JAX-RS(如Quarkus、Helidon),则通过@Provider实现ExceptionMapper接口:
- 为每种JPA异常写一个
ExceptionMapper<T>实现类,标注@Provider - 在
toResponse()方法中构造Response对象,调用status()设置HTTP状态码,并用entity()写入标准化JSON错误体 - 确保这些Provider被正确扫描注册(如添加
beans.xml或启用自动发现)

















