Выбрать главу
Примечание

Реализация ограничения считается управляемым компонентом. Это означает, что с ней вы можете использовать все сервисы, доступные для обработки управляемых компонентов — в частности, внедрение любого вспомогательного класса, EJB или даже EntityManager (подробнее об этом — в следующих главах). Вы также можете перехватывать или декорировать методы initialize и isValid и даже задействовать управление жизненным циклом (@PostConstruct и @PreDestroy).

Множественные ограничения для одной целевой сущности

Иногда полезно применять одинаковое ограничение к одной и той же цели, используя при этом разные свойства или группы (подробнее об этом ниже). Распространенный пример такого рода — ограничение @Pattern, проверяющее соответствие целевой сущности определенному регулярному выражению. В листинге 3.9 показано, как применить два регулярных выражения к одному и тому же атрибуту. Множественные ограничения используют оператор AND. Это означает, что для валидности атрибута orderId необходимо, чтобы он удовлетворял двум регулярным выражениям.

Листинг 3.9. Объект POJO, в котором несколько шаблонных ограничений применяются к одному и тому же атрибуту

public class Order {

··@Pattern.List({

······@Pattern(regexp = "[C,D,M][A-Z][0–9]*"),

······@Pattern(regexp = ".[A-Z].*?")

··})

··private String orderId;

··private Date creationDate;

··private Double totalAmount;

··private Date paymentDate;

··private Date deliveryDate;

··private List<OrderLine> orderLines;

··// Конструкторы, геттеры, сеттеры

}

Чтобы иметь возможность несколько раз применить одно и то же ограничение к данной цели, ограничивающая аннотация должна определить массив на основе самой себя. При валидации компонентов такие массивы ограничений обрабатываются по-особому: каждый элемент массива интерпретируется как обычное ограничение. В листинге 3.10 показана ограничивающая аннотация @Pattern, определяющая внутренний интерфейс (произвольно названный List) с элементом Pattern[]. Внутренний интерфейс должен иметь правило хранения RUNTIME и использовать в качестве исходного ограничения один и тот же набор целей (в данном случае METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER).

Листинг 3.10. Шаблонное ограничение, определяющее список шаблонов

@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})

@Retention(RUNTIME)

@Constraint(validatedBy = PatternValidator.class)

public @interface Pattern {

··String regexp();

··String message() default "{javax.validation.constraints.Pattern.message}";

··Class<?>[] groups() default {};

··Class<? extends Payload>[] payload() default {};

··// Определяет несколько аннотаций @Pattern, применяемых к одному элементу

··@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})

··@Retention(RUNTIME)

··@interface List {

····Pattern[] value();

··}

}

Примечание

При разработке собственной ограничивающей аннотации следует добавить соответствующую ей аннотацию с множеством значений. Спецификация Bean Validation не требует этого строго, но настоятельно рекомендует определять внутренний интерфейс под названием List.

Ограничения на уровне класса

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

В листинге 3.11 показан класс для оформления заказа. Этот заказ товара следует определенному жизненному циклу бизнес-логики: создается в системе, оплачивается клиентом, а потом доставляется клиенту. Класс отслеживает все эти события, оперируя соответствующими датами: creationDate, paymentDate и deliveryDate. Аннотация @ChronologicalDates действует на уровне класса и проверяет, находятся ли эти даты в правильном хронологическом порядке.

Листинг 3.11. Ограничение, действующее на уровне класса и проверяющее верность хронологической последовательности дат

@ChronologicalDates

public class Order {

··private String orderId;

··private Double totalAmount;

··private Date creationDate;

··private Date paymentDate;

··private Date deliveryDate;

··private List<OrderLine> orderLines;

··// Конструкторы, геттеры, сеттеры

}