Спящий режим - есть ли способ получить имя столбца, для которого ограничение было нарушено? - PullRequest
1 голос
/ 02 мая 2020

Я использую Hibernate для сохранения сущностей в базе данных.

В полях сущности (столбцах) я определил множество ограничений (например, NotNull или length = 10). При нарушении ограничения возникает исключение ConstraintViolationException. Я хочу как-то иметь возможность отслеживать, для какого столбца было нарушено ограничение. В ConstraintViolationException в Java -библиотеке "javax.validation.ConstraintViolation" есть метод "getConstraintViolations ()", поэтому я могу просто сделать

constraintViolationException.getConstraintViolations().getPropertyPath()

который должен дать мне имя столбца.

Однако, ConstraintViolationException, выданный Hibernate, не имеет метода "getConstraintViolations ()", и, похоже, ничего подобного нет. Теперь я могу только проанализировать возвращаемую строку, вызвав "getConstraintName ()":

constraintViolationException.getConstraintName()

Но это выглядит довольно грязно и небезопасно.

Кто-нибудь знает, как я могу получить имя столбца?

РЕДАКТИРОВАТЬ:

Соответствующие зависимости внутри пом. xml:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
        </dependency>

Ответы [ 2 ]

1 голос
/ 02 мая 2020

Вы смешиваете вещи.

Проверка бинов

API Bean Validation позволяет проверять ваши бины с помощью @NotNull, @Size и все остальные аннотации пакета javax.validation.constraints.

Вы можете использовать Validator для проверки ваших бобов и получить javax.validation.ConstraintViolationException со всеми ConstraintViolation с информацией о свойстве.

JPA / Hibernate поддерживает API Bean Validation. Если элемент <validation-mode> в persistence.xml установлен на AUTO (или не установлен, так как AUTO является значением по умолчанию), JPA проверит сущности перед сохранением их с Validator и выдает javax.validation.ConstraintViolationException если есть какие-либо ConstraintViolation с, только если в среде присутствует реализация Bean Validation. Если вы установите <validation-mode> на CALLBACK, JPA всегда будет проверять сущности и будет выдавать ошибку во время развертывания, если в среде нет реализации Bean Validation.

SQL Ограничения

Аннотации JPA, такие как @Column, предоставляют информацию для создания базы данных DDL. Использование @Column(nullable = false, length = 10) создаст столбец базы данных с максимальной длиной 10 и ограничением NOT NULL SQL. Но это все. Hibernate не проверять это перед отправкой оператора в базу данных.

Если указано значение null, база данных сообщит об ошибке SQL, которая будет сопоставлена ​​с SQLException драйвером JDB C. Сообщение об ошибке в SQLException будет отличаться для каждой СУБД, но будет содержать имя ограничения. Hibernate оборачивает это исключение в org.hibernate.exception.ConstraintViolationException, который извлекает имя ограничения из сообщения SQLException.

Заключение

  • Установите <validation-mode> на CALLBACK на вашем persistence.xml, чтобы убедиться, что проверка всегда выполняется и недопустимые значения не попадают в база данных
  • Используйте аннотации javax.validation.constraints для обработки ограничений столбца SQL, например, ненулевой столбец с максимальным размером 50:

    @ NotNull @Size (max = 50) @Column (name = "NAME", nullable = false, length = 50)

  • Укажите значимые имена для уникальных ограничений и ограничений внешнего ключа, так как это будет единственной информацией Вы можете определить, какое ограничение нарушено.

0 голосов
/ 02 мая 2020

Ответ от Areus объясняет, что не следует путать валидатор Hibernate и ограничения базы данных, но, опираясь на это, не всегда легко получить имя затронутого столбца из полученного исключения базы данных. Например, когда у вас есть уникальное ограничение для столбца базы данных, трудно правильно вернуть это исключение пользователю.

То, что я сделал, было то, что у меня был класс, который обрабатывает мою обработку исключений вызова REST и находит, что специфицирует c сообщение об исключении. Я не нашел другого способа сделать это в Spring / Hibernate.

@ControllerAdvice
@RequestMapping(produces = APPLICATION_JSON_VALUE)
public class RestResponseEntityExceptionHandler {

    @ExceptionHandler(DataIntegrityViolationException.class)
    public ResponseEntity<ErrorDTO> dataIntegrityViolationException(final DataIntegrityViolationException e) {
        String mostSpecificCauseMessage = e.getMostSpecificCause().getMessage();
        if (e.getCause() instanceof ConstraintViolationException) {
            String name = ((ConstraintViolationException) e.getCause()).getConstraintName();
            log.debug("Encountered ConstraintViolationException, details: " + mostSpecificCauseMessage);
            return determineError(mostSpecificCauseMessage, name);
        } else {
            log.debug("Encountered DataIntegrityViolation exception, details: " + mostSpecificCauseMessage);
            ErrorDTO errorDTO =
                new ErrorDTO("BAD_REQUEST_ERROR", "DataIntegrityViolation exception. " + mostSpecificCauseMessage);
            return new ResponseEntity<>(errorDTO, HttpStatus.BAD_REQUEST);
        }
    }

    //this catches the manually invoked entityManager.flush() exceptions
    @ExceptionHandler(ConstraintViolationException.class)
    public ResponseEntity<ErrorDTO> constraintViolationException(final ConstraintViolationException e) {
        String message = e.getMessage();
        String name = e.getConstraintName();
        log.debug("Encountered ConstraintViolationException, details: {}", message);
        return determineError(message, name);
    }

    private ResponseEntity<ErrorDTO> determineError(String message, String name) {
        Map<String, String> errors = new HashMap<>();
        switch (name) {
            case Project.UNIQUE_CONSTRAINT_NAME:
                errors.put("name", ALREADY_EXISTS);
                break;
            case Value.UNIQUE_CONSTRAINT_VALUE:
                errors.put("value", ALREADY_EXISTS);
                break;
            //and so on
            default:
                break;
        }

        if (!errors.isEmpty()) {
            ErrorDTO error = new ErrorDTO(VALIDATION_ERROR, VALIDATION_ERROR_MESSAGE, errors);
            return new ResponseEntity<>(error, HttpStatus.BAD_REQUEST);
        } else {
            ErrorDTO errorDTO = new ErrorDTO("CONFLICT_ERROR", message);
            return new ResponseEntity<>(errorDTO, HttpStatus.CONFLICT);
        }
    }
}

Вам также нужно определить имя ограничения в вашей сущности / базе данных модели, например:

@Table(uniqueConstraints = {
    @UniqueConstraint(name = UNIQUE_CONSTRAINT_VALUE, columnNames = {"value"})})

public static final String UNIQUE_CONSTRAINT_VALUE = "unique_constraint_property_value";

И в определении базы данных, конечно же.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...