····int adjust = 0; if (now.get(DAY_OF_YEAR) — birth.get(DAY_OF_YEAR) < 0) {
······adjust = -1;
····}
····customer.setAge(now.get(YEAR) — birth.get(YEAR) + adjust);
··}
}
public class DataValidationListener {
··@PrePersist
··@PreUpdate
··private void validate(Customer customer) {
····if (customer.getFirstName() == null || "".equals(customer.getFirstName()))
······throw new IllegalArgumentException("Неверное имя");
····if (customer.getLastName() == null || "".equals(customer.getLastName()))
······throw new IllegalArgumentException("Неверная фамилия");
··}
}
К классу-слушателю применяются только простые правила. Первое правило состоит в том, что класс должен располагать конструктором public без аргументов. Второе правило заключается в том, что подписи методов обратного вызова немного отличаются от тех, что приведены в листинге 6.38. Когда вы вызываете метод обратного вызова в слушателе, этому методу необходимо иметь доступ к состоянию сущности (например, к firstName и lastName сущности Customer, которые необходимо подвергнуть валидации). Методы должны располагать параметром, имеющим тип, который совместим с типом сущности, поскольку сущность, связанная с соответствующим событием, передается в обратный вызов. Метод обратного вызова, определенный в сущности, будет иметь такую подпись без параметров:
void <МЕТОД>();
Методы обратного вызова, определенные для слушателя сущности, могут иметь подписи двух разных типов. Если метод будет использоваться в нескольких сущностях, то у него должен быть аргумент Object:
void <МЕТОД>(Object anyEntity)
Если он предназначен только для одной сущности или ее подклассов (при наследовании), то параметр может иметь тип сущности:
void <МЕТОД>(Customer customerOrSubclasses)
Для обозначения того, что эти два слушателя будут уведомляться о событиях жизненного цикла сущности Customer, вам необходимо использовать аннотацию @EntityListeners (листинг 6.41). Она может принимать в качестве параметра один слушатель сущности либо массив слушателей. Если будет определено несколько слушателей и произойдет событие жизненного цикла, то поставщик постоянства произведет итерацию по каждому слушателю в том порядке, в котором они указаны, и вызовет метод обратного вызова, передав ссылку на сущность, к которой относится соответствующее событие. Затем он вызовет методы обратного вызова в самой сущности (при наличии таковых).
@EntityListeners({DataValidationListener.class, AgeCalculationListener.class})
@Entity
public class Customer {
··@Id @GeneratedValue
··private Long id;
··private String firstName;
··private String lastName;
··private String email;
··private String phoneNumber;
··@Temporal(TemporalType.DATE)
··private Date dateOfBirth;
··@Transient
··private Integer age;
··@Temporal(TemporalType.TIMESTAMP)
··private Date creationDate;
··// Конструкторы, геттеры, сеттеры
}
Результат выполнения этого кода будет точно таким же, что и кода из листинга 6.38. Сущность Customer валидирует свои данные перед вставкой или обновлением с использованием метода DataValidationListener.validate() и вычислит значение своего age с помощью метода слушателя AgeCalculationListener.calculateAge().
Правила, которых должны придерживаться методы слушателя сущности, аналогичны правилам для методов обратного вызова сущности, за исключением нескольких деталей.
• Могут генерироваться только непроверяемые исключения. Это приводит к тому, что остальные слушатели и методы обратного вызова не вызываются, а транзакция подвергается откату (при наличии таковой).