Spring Boot Rest API处理唯一约束时自定义响应的两个问题
问题1:全局统一异常处理实现
使用Spring Boot自带的@RestControllerAdvice注解即可实现全局异常统一拦截处理,无需在每个业务方法中单独编写try catch逻辑,实现代码如下:
import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.dao.DataIntegrityViolationException; // 不同数据库对应的异常类可自行替换,示例为H2数据库 import org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException; import java.util.Map; @RestControllerAdvice public class GlobalExceptionHandler { // 处理数据完整性相关异常(包含唯一约束冲突) @ExceptionHandler(DataIntegrityViolationException.class) public ResponseEntity<Map<String, String>> handleConstraintViolation(DataIntegrityViolationException e) { // 提取原始SQL层异常 if (e.getCause() instanceof JdbcSQLIntegrityConstraintViolationException sqlException) { String errorMsg = sqlException.getMessage(); // 根据自定义约束名判断冲突字段 if (errorMsg.contains("uk_user_email")) { return ResponseEntity.status(HttpStatus.CONFLICT) .body(Map.of("message", "邮箱已被注册")); } if (errorMsg.contains("uk_user_username")) { return ResponseEntity.status(HttpStatus.CONFLICT) .body(Map.of("message", "用户名已存在")); } } // 其他数据异常返回通用提示 return ResponseEntity.status(HttpStatus.BAD_REQUEST) .body(Map.of("message", "请求参数不符合规则")); } }
如果使用其他数据库,只需要替换JdbcSQLIntegrityConstraintViolationException为对应数据库的约束冲突异常类即可,比如MySQL对应的是com.mysql.cj.jdbc.exceptions.SQLIntegrityConstraintViolationException。
问题2:精准区分唯一约束冲突字段
无需增加额外的前置查询,通过自定义唯一约束名称+解析异常信息的方式即可实现,没有额外开销,还能避免前置查询的竞态问题,实现步骤如下:
步骤1:自定义唯一约束名称
修改User实体类的表配置,给两个唯一约束指定固定可识别的名称,替换原来的@Column(unique = true)配置:
@Entity @NoArgsConstructor @Getter @Setter @Table(uniqueConstraints = { @UniqueConstraint(name = "uk_user_email", columnNames = "email"), @UniqueConstraint(name = "uk_user_username", columnNames = "username") }) public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @NotNull private String email; @NotNull private String username; @NotNull private String password; public User(String email, String username, String password) { this.email = email; this.password = password; this.username = username; } }
如果是手动建表而非JPA自动建表,需要保证手动创建的唯一约束名和上面配置的名称一致。
步骤2:异常解析判断
上面全局异常处理器中的代码已经包含了约束名判断逻辑,所有主流关系型数据库违反唯一约束时的异常信息都会携带约束名称,该方案通用。
内容的提问来源于stack exchange,提问作者Apri
相关产品推荐
相关产品推荐

