«1. Обзор

В этом руководстве мы рассмотрим ограничения Hibernate Validator, которые встроены в Hibernate Validator, но не входят в спецификацию Bean Validation.

Краткий обзор проверки бинов можно найти в нашей статье Основы валидации бинов Java.

2. Настройка Hibernate Validator

По крайней мере, мы должны добавить Hibernate Validator в наши зависимости:

<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.0.16.Final</version>
</dependency>

Обратите внимание, что Hibernate Validator не зависит от Hibernate, ORM, который мы рассмотрели в многие другие статьи.

Кроме того, некоторые аннотации, которые мы представим, применимы только в том случае, если наш проект использует определенные библиотеки. Итак, для каждого из них мы укажем необходимые зависимости.

3. Проверка денежных ценностей

3.1. Проверка номеров кредитных карт

Действительные номера кредитных карт должны соответствовать контрольной сумме, которую мы вычисляем с помощью алгоритма Луна. Ограничение @CreditCardNumber выполняется успешно, когда строка удовлетворяет контрольной сумме.

@CreditCardNumber не выполняет никакой другой проверки входной строки. В частности, он не проверяет длину ввода. Поэтому он может обнаруживать только числа, которые недействительны из-за небольшой опечатки.

Обратите внимание, что по умолчанию ограничение не работает, если строка содержит символы, не являющиеся цифрами, но мы можем указать, чтобы она их игнорировала:

@CreditCardNumber(ignoreNonDigitCharacters = true)
private String lenientCreditCardNumber;

Затем мы можем включить такие символы, как пробелы или тире:

validations.setLenientCreditCardNumber("7992-7398-713");
constraintViolations = validator.validateProperty(validations, "lenientCreditCardNumber");
assertTrue(constraintViolations.isEmpty());

3.2. Проверка денежных значений

Валидатор @Currency проверяет, выражена ли данная денежная сумма в указанной валюте:

@Currency("EUR")
private MonetaryAmount balance;

Класс MonetaryAmount является частью Java Money. Поэтому @Currency применяется только тогда, когда доступна реализация Java Money.

После правильной настройки Java Money можно проверить ограничение:

bean.setBalance(Money.of(new BigDecimal(100.0), Monetary.getCurrency("EUR")));
constraintViolations = validator.validateProperty(bean, "balance");
assertEquals(0, constraintViolations.size());

4. Проверка диапазонов

4.1. Числовые и денежные диапазоны

Спецификация проверки бина определяет несколько ограничений, которые мы можем применить к числовым полям. Помимо этого, Hibernate Validator предоставляет удобную аннотацию @Range, которая действует как комбинация @Min и @Max, включая диапазон:

@Range(min = 0, max = 100)
private BigDecimal percent;

Подобно @Min и @Max, @Range применим к полям примитивные числовые типы и их оболочки; BigInteger и BigDecimal, строковые представления вышеперечисленного и, наконец, поля MonetaryValue.

4.2. Продолжительность времени

В дополнение к стандартным аннотациям JSR 380 для значений, представляющих моменты времени, Hibernate Validator также включает ограничения для продолжительности. Обязательно сначала ознакомьтесь с классами Period и Duration Java Time.

Итак, мы можем установить для свойства минимальную и максимальную продолжительность:

@DurationMin(days = 1, hours = 2)
@DurationMax(days = 2, hours = 1)
private Duration duration;

Даже если мы не покажем их все здесь, в аннотации есть параметры для всех единиц времени от наносекунд до дней.

Обратите внимание, что по умолчанию минимальные и максимальные значения включаются. То есть значение, которое точно совпадает с минимальным или максимальным значением, пройдет проверку.

Если мы хотим, чтобы граничные значения были недействительными, вместо этого мы определяем включающее свойство как false:

@DurationMax(minutes = 30, inclusive = false)

5. Проверка строк

5.1. Длина строки

Мы можем использовать два немного разных ограничения, чтобы обеспечить заданную длину строки.

Как правило, мы хотим убедиться, что длина строки в символах — та, которую мы измеряем с помощью метода длины, — находится между минимумом и максимумом. В этом случае мы используем @Length для свойства или поля String:

@Length(min = 1, max = 3)
private String someString;

Однако из-за сложностей Unicode иногда длина в символах и длина в кодовых точках различаются. Когда мы хотим проверить последнее, мы используем @CodePointLength:

@CodePointLength(min = 1, max = 3)
private String someString;

Например, строка «aa\\uD835\\uDD0A» имеет длину 4 символа, но содержит только 3 кодовых точки, поэтому она не будет выполнена. первое ограничение и передать второе.

Кроме того, с обеими аннотациями мы можем опустить минимальное или максимальное значение.

5.2. Проверка строк цифр

Мы уже видели, как проверить, что строка является действительным номером кредитной карты. Однако Hibernate Validator включает несколько других ограничений для строк цифр.

«Первым, кого мы рассматриваем, является @LuhnCheck. Это обобщенная версия @CreditCardNumber, поскольку она выполняет ту же проверку, но допускает дополнительные параметры:

@LuhnCheck(startIndex = 0, endIndex = Integer.MAX_VALUE, checkDigitIndex = -1)
private String someString;

Здесь мы показали значения параметров по умолчанию, поэтому приведенное выше эквивалентно простому @LuhnCheck аннотация.

Но, как мы видим, мы можем выполнить проверку подстроки (startIndex и endIndex) и сообщить ограничению, какая цифра является цифрой контрольной суммы, где -1 означает последнюю цифру в проверяемой подстроке.

Другие интересные ограничения включают проверку по модулю 10 (@Mod10Check) и проверку по модулю 11 (@Mod11Check), которые обычно используются для штрих-кодов и других кодов, таких как ISBN.

Однако для этих конкретных случаев Hibernate Validator предоставляет ограничение для проверки кодов ISBN, @ISBN, а также ограничение @EAN для штрих-кодов EAN.

5.3. Проверка URL и HTML

Ограничение @Url проверяет, является ли строка допустимым представлением URL. Кроме того, мы можем проверить, что определенный компонент URL-адреса имеет определенное значение:

@URL(protocol = "https")
private String url;

Таким образом, мы можем проверить протокол, хост и порт. Если этого недостаточно, есть свойство регулярного выражения, которое мы можем использовать для сопоставления URL-адреса с регулярным выражением.

Мы также можем проверить, что свойство содержит «безопасный» HTML-код (например, без тегов сценария):

@SafeHtml
private String html;

@SafeHtml использует библиотеку JSoup, которая должна быть включена в наши зависимости.

Мы можем настроить очистку HTML в соответствии с нашими потребностями, используя встроенные белые списки тегов (свойство whitelist аннотации) и включая дополнительные теги и атрибуты (параметры AdditionalTags и AdditionalTagsWithAttributes).

6. Другие ограничения

Кратко отметим, что Hibernate Validator включает в себя некоторые ограничения, зависящие от страны и региона, в частности, для некоторых бразильских и польских идентификационных номеров, кодов налогоплательщиков и т.п. Пожалуйста, обратитесь к соответствующему разделу документации для получения полного списка.

Также мы можем проверить, что коллекция не содержит дубликатов с помощью @UniqueElements.

Наконец, для сложных случаев, не охваченных существующими аннотациями, мы можем вызвать сценарий, написанный в совместимом с JSR-223 механизме сценариев. Мы, конечно же, затронули JSR-223 в нашей статье о Nashorn, реализации JavaScript, включенной в современные JVM.

В этом случае аннотация находится на уровне класса, а скрипт вызывается для всего экземпляра, переданного как переменная _this:

@ScriptAssert(lang = "nashorn", script = "_this.valid")
public class AdditionalValidations {
    private boolean valid = true;
    // standard getters and setters
}

Затем мы можем проверить ограничение для всего экземпляра: ~ ~~

bean.setValid(false);
constraintViolations = validator.validate(bean);
assertEquals(1, constraintViolations.size());

7. Заключение

В этой статье мы перечислили ограничения в Hibernate Validator, которые выходят за рамки минимального набора, определенного в спецификации Bean Validation.

Реализацию всех этих примеров и фрагментов кода можно найти на GitHub.