Spring MVC + Hibernate: стратегии проверки данных
Все мы знаем, что Spring MVC хорошо интегрируется с Hibernate Validator и JSR-303 в целом. Но Hibernate Validator, как кто-то сказал, предназначен только для проверки компонентов, что означает, что более сложные проверки должны передаваться на уровень данных. Примеры таких проверок: уникальность бизнес-ключа, зависимость внутри записей (что обычно указывает на проблемы проектирования БД, но мы все живем в несовершенном мире). Даже простые проверки, такие как длина строкового поля, могут определяться некоторым значением БД, что делает Hibernate Validator непригодным для использования.
Итак, мой вопрос: есть ли что-то, что предлагает Spring, Hibernate или JSR для выполнения таких сложных проверок? Существует ли какой-либо установленный шаблон или технологический элемент для выполнения такой проверки в стандартной настройке Controller-Service-Repository на основе Spring и Hibernate?
ОБНОВЛЕНИЕ: Позвольте мне быть более конкретным. Например, есть форма, которая отправляет запрос сохранения AJAX на контроллер save
метод. Если возникает какая-либо ошибка проверки - простая или "сложная" - мы должны вернуться к браузеру с некоторым json, указывающим на проблемное поле и связанную ошибку. Для простых ошибок я могу извлечь поле (если есть) и сообщение об ошибке из BindingResult
, Какую инфраструктуру (возможно, конкретную, а не специальную исключительную ситуацию?) Вы бы предложили для "сложных" ошибок? Использование обработчика исключений не кажется мне хорошей идеей, потому что разделение одного процесса проверки между save
метод и @ExceptionHandler
делает вещи запутанными. В настоящее время я использую некоторые специальные исключения (например, ValidationException
):
public @ResponseBody Result save(@Valid Entity entity, BindingResult errors) {
Result r = new Result();
if (errors.hasErrors()) {
r.setStatus(Result.VALIDATION_ERROR);
// ...
} else {
try {
dao.save(entity);
r.setStatus(Result.SUCCESS);
} except (ValidationException e) {
r.setStatus(Result.VALIDATION_ERROR);
r.setText(e.getMessage());
}
}
return r;
}
Можете ли вы предложить более оптимальный подход?
1 ответ
Да, есть старый добрый установленный шаблон Java для выдачи исключений.
Spring MVC интегрирует его довольно хорошо (для примеров кода вы можете сразу перейти ко второй части моего ответа).
То, что вы называете "сложной проверкой", на самом деле является исключением: ошибка уникальности бизнес-ключа, ошибки низкого уровня или ошибки БД и т. Д.
Напоминание: что такое валидация в Spring MVC?
Проверка должна происходить на уровне представления. В основном это касается проверки отправленных полей формы.
Мы могли бы классифицировать их на два вида:
1) Легкая проверка (с проверкой JSR-303/Hibernate): проверка того, что отправленное поле имеет заданный @Size
/ @Length
, что это @NotNull
или же @NotEmpty
/ @NotBlank
, проверяя, что у него есть @Email
формат и т. д.
2) Обширная проверка или сложная проверка - это больше о конкретных случаях проверки полей, таких как проверка между полями:
- Пример 1: форма имеет
fieldA
,fieldB
а такжеfieldC
, По отдельности каждое поле может быть пустым, но хотя бы одно из них не должно быть пустым. - Пример 2: если
userAge
поле имеет значение до 18,responsibleUser
поле не должно быть нулевым иresponsibleUser
возраст должен быть старше 21 года.
Эти проверки могут быть реализованы с помощью реализаций Spring Validator или пользовательских аннотаций / ограничений.
Теперь я понимаю, что со всеми этими средствами проверки, а также с тем фактом, что Spring совсем не навязчив и позволяет вам делать все, что угодно (к лучшему или к худшему), можно испытать искушение использовать "молоток проверки" для чего-либо неопределенно связанного к обработке ошибок.
И это сработало бы: только с проверкой вы проверяете каждую возможную проблему в ваших валидаторах / аннотациях (и вряд ли выбрасываете какие-либо исключения на более низких уровнях). Это плохо, потому что вы молитесь, чтобы вы подумали обо всех случаях. Вы не используете исключения Java, которые позволили бы вам упростить вашу логику и уменьшить вероятность ошибки, забыв проверить, что что-то имело ошибку.
Таким образом, в мире Spring MVC не следует путать валидацию (то есть валидацию пользовательского интерфейса) для исключений нижнего уровня, таких как исключения Service или исключения DB (уникальность ключа и т. Д.).
Как обработать исключения в Spring MVC удобным способом?
Некоторые люди думают: "О боже, так что в моем контроллере я должен был бы проверить все возможные проверенные исключения по одному и подумать об ошибке сообщения для каждого из них? НЕТ!". Я один из тех людей.:-)
В большинстве случаев просто используйте какой-то общий проверенный класс исключений, который расширит все ваши исключения. Затем просто обработайте его в своем контроллере Spring MVC с помощью @ExceptionHandler и общего сообщения об ошибке.
Пример кода:
public class MyAppTechnicalException extends Exception { ... }
а также
@Controller
public class MyController {
...
@RequestMapping(...)
public void createMyObject(...) throws MyAppTechnicalException {
...
someServiceThanCanThrowMyAppTechnicalException.create(...);
...
}
...
@ExceptionHandler(MyAppTechnicalException.class)
public String handleMyAppTechnicalException(MyAppTechnicalException e, Model model) {
// Compute your generic error message/code with e.
// Or just use a generic error/code, in which case you can remove e from the parameters
String genericErrorMessage = "Some technical exception has occured blah blah blah" ;
// There are many other ways to pass an error to the view, but you get the idea
model.addAttribute("myErrors", genericErrorMessage);
return "myView";
}
}
Просто, быстро, легко и чисто!
В тех случаях, когда вам необходимо отобразить сообщения об ошибках для некоторых конкретных исключений или когда вы не можете иметь общее исключение верхнего уровня из-за плохо разработанной устаревшей системы, которую вы не можете изменить, просто добавьте другие @ExceptionHandler
s.
Другой трюк: для менее загроможденного кода вы можете обрабатывать несколько исключений с
@ExceptionHandler({MyException1.class, MyException2.class, ...})
public String yourMethod(Exception e, Model model) {
...
}
Итог: когда использовать валидацию? когда использовать исключения?
- Ошибки из пользовательского интерфейса = проверка = средства проверки (аннотации JSR-303, пользовательские аннотации, валидатор Spring)
- Ошибки из нижних уровней = исключения
Когда я говорю "Ошибки в пользовательском интерфейсе", я имею в виду "пользователь ввел что-то неправильное в своей форме".
Рекомендации: