Четкое отображение нарушений ограничений БД, отсутствие имитации ограничений в приложении.JAVA

Программисты JAVA общаются здесь
Anonymous
Четкое отображение нарушений ограничений БД, отсутствие имитации ограничений в приложении.

Сообщение Anonymous »

Код: Выделить всё

user.name
имеет ограничение UNIQUE для БД (PG).
Я улавливаю исключение DataIntegrityViolationException в своем глобальном обработчике:

Код: Выделить всё

    @ExceptionHandler(DataIntegrityViolationException.class)
public ResponseEntity handle(DataIntegrityViolationException ex) {
ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(HttpStatus.UNPROCESSABLE_CONTENT, ex.getMessage());
return ResponseEntity.unprocessableContent().body(problemDetail);
}
Пример ответа при попытке вставить пользователя с тем же именем пользователя:

Код: Выделить всё

{
"detail": "could not execute statement [ОШИБКА: повторяющееся значение ключа нарушает ограничение уникальности \"user_name_key\"\n  Подробности: Ключ \"(name)=(jack)\" уже существует.] [insert into \"user\" (name,password,id) values (?,?,?)]; SQL [insert into \"user\" (name,password,id) values (?,?,?)]; constraint [null]",
"instance": "/api/admin/users",
"status": 422,
"title": "Unprocessable Content"
}
Обратите внимание, насколько многословны и неуклюжи детали. К тому же он на русском (я думаю, локаль), а ошибки выдаю на английском.
Что посоветуете? Вручную проверять его в моем сервисе и выбрасывать исключение вручную? Но тогда мне пришлось бы вручную синхронизировать ограничения БД и мою логику проверки. Например, если есть новый столбец UNIQUE, мне придется обновить и перестроить службу. Выглядит неоптимально.
Клод Сонет предложил следующее:

Код: Выделить всё

private String resolveDetail(DataIntegrityViolationException ex) {
if (ex.getCause() instanceof ConstraintViolationException cve) {
return switch (cve.getSQLState()) {
case "23505" -> resolveUniqueViolation(cve.getConstraintName());
case "23503" -> "Operation violates a referential integrity constraint";
case "23502" -> "A required field is missing";
case "23514" -> "A value failed a check constraint";
default -> "A database constraint was violated";
};
}
return "Data integrity violation";
}

private String resolveUniqueViolation(String constraintName) {
return appProperties.getConstraintMessages()
.getOrDefault(constraintName, "A unique constraint was violated");
}

Код: Выделить всё

app:
constraint-messages:
user_name_key: "Username is already taken"
Можете себе представить, что меня не в восторге от такого подхода.
Я могу это сделать:

Код: Выделить всё

    @ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity handle(ConstraintViolationException ex) {
String message = String.format("%s constraint violation", ex.getKind());
ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(HttpStatus.UNPROCESSABLE_CONTENT, message);
return ResponseEntity.unprocessableContent().body(problemDetail);
}

Код: Выделить всё

{
"detail": "UNIQUE constraint violation",
"instance": "/api/admin/users",
"status": 422,
"title": "Unprocessable Content"
}
Недостаток в том, что он неинформативен и больше связывает мое приложение с Hibernate (

Код: Выделить всё

ConstraintViolationException
находится в спящем режиме).Spring Boot 4, Webp>

Вернуться в «JAVA»