Bean Validation 2.0 – Groups & Payload
Merhaba arkadaşlar. Bean validation serimizin son makalesinde groups ve payload kullanımını incelemeye çalışacağız. Groups, validasyonları gruplamamıza yarayan özelliktir. Örneğin Article nesnemizin title değerinin benzersiz olmasını istiyoruz. Bunun içinde veritabanından bu değeri kontrol etmemiz gerekmekte. Bu işlemi custom bir constraint yazarak çözebiliriz. Bir önceki makalemizde bu konuya göz atmıştık. Burada dikkat etmemiz gereken bir konu şu ki; veritabanı sorgusu bir maliyet içermektedir. Veritabanı, dosya okuma, soket ile erişim vb işlemler maliyetlidir. Bu sebeple bu tarz maliyetli validasyonları hatasız biten standart validasyonlarının ardından yapmamız performansımızı olumlu etkileyecektir. Tam bu noktada da groups özelliği devreye girer. Kodumuzu yazarak nasıl yapacağımızı görelim.
ilk olarak article sınıfımız için groups olarak kullanılacak bir interface ekliyeceğiz. Ardından UniqueTitle isimli validasyon anotasyonu ve UniqueTitleValidator isimli validator sınıfımız oluşturacağız. Article içindeki title alanını UniqueTitle ile etiketleyeceğiz. Son olarak kodumuzu çalıştıracağız.
UniqueTitle anotasyon:
1 2 3 4 5 6 7 8 9 10 11 12 |
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy=UniqueTitleValidator.class) public @interface UniqueTitle { String message() default ""; Class<? extends Payload>[] payload() default {}; Class<?>[] groups() default {}; } |
UniqueTitleValidator sınıfı:
1 2 3 4 5 6 7 8 9 10 11 |
public class UniqueTitleValidator implements ConstraintValidator<UniqueTitle, String> { @Override public boolean isValid(String value, ConstraintValidatorContext context) { System.out.println("Title Value : " + value + " is valid"); return false; } } |
UniqueTitle aatasyonumuza, validator sınıfımızı veriyoruz.
1 2 3 4 5 |
@Constraint(validatedBy=UniqueTitleValidator.class) |
Article sınıfımızın son hali.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
@NotBlank(message = "author alanı boş olamaz!") private String author; @NotNull(message = "Subject alanı boş olamaz") @Pattern(regexp="A|B|C") private String subject; @NotEmpty(message = "category listesi bos olamaz") private List<@NotNull(message = "category elemanları null olamaz") String> category; @UniqueTitle(groups=Article.TitleDbValidator.class, message="Title benzersiz olmalı") private String title; @FutureOrPresent(message = "lastSaveDate geçmişte olamaz") private LocalDate lastSaveDate; private Boolean publish; @NotBlank(message = "Text alanı null olamaz") private String text; public interface TitleDbValidator { } |
Kodumuzu çalıştıracağımız Starter sınıfı;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public static void main(String[] args) { Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); Set<ConstraintViolation<Article>> violations = validator.validate(initializeInstance(), Article.TitleDbValidator.class); violations.forEach(violation -> { System.out.println(violation.getMessage()); violation.getConstraintDescriptor().getGroups().forEach(group -> { System.out.println(group.getCanonicalName()); }); }); } static Article initializeInstance() { Article article = new Article(); article.setAuthor("Author"); article.setCategory(Arrays.asList("A","B")); article.setLastSaveDate(LocalDate.now()); article.setPublish(false); article.setSubject("A"); article.setText("Text"); article.setTitle("Title"); return article; } |
validator’un validate metotuna TitleDbValidator interface’imizi parametre olarak geçtik. Bunun anlamı Article sınıfında groups özelliğinde TitleDbValidator tipi olan validasyonlar çalıştırılacak. Konsole basılan çıktıya baktığımızda da sadece title validatorun çalıştığını görmüş oluruz. Bu şekilde anotasyonumuza group özelliği tanımlamış olduk. Group için tanımlamış olduğumuz interface’e erişebilir, yazdığımız default metotlar var ise çalıştırabiliriz. Şimdi payload kavramına bakalım.
Payload validasyon hatalarımıza ekstra bilgiler eklememizi sağlar. Benim gördüğüm örneklerde (ve kendi kullanımlarımda), payload ile hatalar derecelendiriliyor. Info, Error ve benzeri şekilde. Bu durumda da donen hatalar da payload değerine göre farklı farklı işlemler yapabilme imkanımız olur. Ayrıca payload için tanımladığımız interface’in metotlarını çalıştırabiliriz. Hem group hem de payload bize validasyon hataları ile ilgili oldukça fazla esneklik sağlar.
Payload interface’lerimizi yapalım.
1 2 3 4 5 6 7 8 9 10 11 |
public interface Severity { public interface Info extends Payload { } public interface Error extends Payload { } } |
Article içindeki @UniqueTitle anotasyonumuza payload bilgisi verelim.
1 2 3 4 5 |
@UniqueTitle(groups=Article.TitleDbValidator.class, message="Title benzersiz olmalı", payload=Severity.Error.class) |
Son olarak Starter sınıfımızdaki değişliği yapalım.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public static void main(String[] args) { Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); Set<ConstraintViolation<Article>> violations = validator.validate(initializeInstance(), Article.TitleDbValidator.class); violations.forEach(violation -> { System.out.println(violation.getMessage()); violation.getConstraintDescriptor().getGroups().forEach(group -> { System.out.println(group.getCanonicalName()); }); <strong>violation.getConstraintDescriptor().getPayload().forEach(payload -> { System.out.println(payload.getCanonicalName()); });</strong> }); } |
Çalıştırdığımızda aşağıdaki sonucu almalıyız.
1 2 3 4 5 6 7 8 |
Title Value : Title is valid Title benzersiz olmalı io.bilisim.entity.Article.TitleDbValidator io.bilisim.validator.payload.Severity.Error |
Group ve payload kavramlarını inceledik. Umarım faydalı olmuştur. Gayret bizden Tevfik Allah’tan.
Kaynak Kod : https://github.com/volkanozdemir/validation-api-grouo-payload.git