Jednak po jakimś czasie używania (jakże przyjemnego) standardowych walidatorów, doszedłem do wniosku, że to za mało. Napisanie kilku własnych, które swoją drogą również tworzy się bardzo prosto, tylko na chwilę zaspokoiło moje potrzeby. Dopiero połączenie JSR 303 i mechanizmu refleksji w javie, daje maksymalne możliwości wykorzystania tego standardu.
Załóżmy, że mamy klasę która posiada dwie daty: od i do.
public class Entity { private Date from; private Date to; }
Chcielibyśmy sprawdzić czy podane daty są po kolei, tj 'from' <= 'to'. Możemy napisać własny walidator dla klasy Entity, ale w projekcie takich klas z datami możemy mieć mnóstwo i dla każdej z nich należałoby stworzyć oddzielny walidator. Dzięki refleksją w javie możemy zrobić jeden uniwersalny, który jako parametry przyjmowałby 2 wartości, nazwa pola 'from', nazwa pola 'to'.
@Target({ TYPE }) @Retention(RUNTIME) @Constraint(validatedBy = CheckDatesValidator.class) public @interface CheckDates { String message() default "{pl.costam.CheckDates}"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; String dateFrom(); String dateTo(); }
I sam walidator:
public class CheckDatesValidator implements ConstraintValidator<CheckDates, Object> { private String dateFromFieldName; private String dateToFieldName; private String message; @Override public void initialize(CheckDates checkDates) { dateFromFieldName = checkDates.dateFrom(); dateToFieldName = checkDates.dateTo(); message = checkDates.message(); } @Override public boolean isValid(Object object, ConstraintValidatorContext context) { boolean result = validateDates(object); if (!result) { //jeśli walidacja nie powiodła sie to tworzymy własny constraint - wskazujemy dla którego pola wystąpił błąd context.disableDefaultConstraintViolation(); context.buildConstraintViolationWithTemplate(message) .addNode(dateToFieldName) .addConstraintViolation(); } return result; } private boolean validateDates(Object object) { try { Date dateFrom = field(dateFromFieldName).ofType(Date.class).in(object).get(); Date dateTo = field(dateToFieldName).ofType(Date.class).in(object).get(); // sprawdzenie dat return checkDatesInterval(dateFrom, dateTo); } catch (Exception e) { e.printStackTrace(); return false; } } }
Poprzez refleksję można odwołać sie do pola używając właściwie "czystej" javy, ale wygląda to średnio w kodzie i lepiej użyć jakiegoś gotowego rozwiązania. Ja osobiście polecam biliotekę: FEST
Zastowanie adnotacji jest następujące:
@CheckDates(dateFrom="from", dateTo="to") public class Entity { private Date from; private Date to; }