İki veya daha fazla alanı birlikte nasıl doğrulayabilirim?


92

Modellerimi doğrulamak için JPA 2.0 / Hibernate doğrulamasını kullanıyorum. Şimdi, iki alanın kombinasyonunun doğrulanması gereken bir durum var:

public class MyModel {
    public Integer getValue1() {
        //...
    }
    public String getValue2() {
        //...
    }
}

Modeli olup geçersiz hem eğer getValue1()ve getValue2()vardır nullve geçerli aksi.

Bu tür bir doğrulamayı JPA 2.0 / Hibernate ile nasıl gerçekleştirebilirim? Basit bir @NotNullek açıklama ile, doğrulamayı geçmek için her iki alıcı da boş olmamalıdır.


Yanıtlar:


102

Birden çok özellik doğrulaması için, sınıf düzeyinde kısıtlamalar kullanmalısınız. Gönderen Bean Doğrulama Hızlı Göz At bölüm II: özel kısıtlamalar :

### Sınıf düzeyinde kısıtlamalar

Bazılarınız, birden çok özelliği kapsayan bir kısıtlama uygulama veya birkaç özelliğe bağlı olan bir kısıtlama ifade etme becerisine ilişkin endişelerini dile getirdiniz. Klasik örnek, adres doğrulamadır. Adreslerin karmaşık kuralları vardır:

  • bir sokak adı biraz standarttır ve kesinlikle bir uzunluk sınırına sahip olmalıdır
  • posta kodu yapısı tamamen ülkeye bağlıdır
  • şehir genellikle bir posta koduyla ilişkilendirilebilir ve bazı hata kontrolleri yapılabilir (bir doğrulama hizmetinin erişilebilir olması şartıyla)
  • Bu karşılıklı bağımlılıklar nedeniyle faturaya uymak için basit bir özellik seviyesi kısıtlaması var

Bean Doğrulama spesifikasyonunun sunduğu çözüm iki yönlüdür:

  • grupların ve grup dizilerinin kullanılması yoluyla bir dizi kısıtlamanın diğer bir kısıtlama setinden önce uygulanmasını zorlama yeteneği sunar. Bu konu bir sonraki blog girişinde ele alınacak
  • sınıf düzeyinde kısıtlamaların tanımlanmasına izin verir

Sınıf düzeyinde kısıtlamalar, bir özellik yerine bir sınıfa uygulanan normal kısıtlamalardır (açıklama / uygulama ikilisi). Farklı bir şekilde söylenirse, sınıf düzeyindeki kısıtlamalar içinde nesne örneğini (özellik değeri yerine) alır isValid.

@AddressAnnotation 
public class Address {
    @NotNull @Max(50) private String street1;
    @Max(50) private String street2;
    @Max(10) @NotNull private String zipCode;
    @Max(20) @NotNull String city;
    @NotNull private Country country;
    
    ...
}

@Constraint(validatedBy = MultiCountryAddressValidator.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AddressAnnotation {
    String message() default "{error.address}";
    Class<?>[] groups() default { };
    Class<? extends Payload>[] payload() default { };
}

public class MultiCountryAddressValidator implements ConstraintValidator<AddressAnnotation, Address> {
    public void initialize(AddressAnnotation constraintAnnotation) {
    // initialize the zipcode/city/country correlation service
    }

    /**
     * Validate zipcode and city depending on the country
     */
    public boolean isValid(Address object, ConstraintValidatorContext context) {
        if (!(object instanceof Address)) {
            throw new IllegalArgumentException("@Address only applies to Address");
        }
        Address address = (Address) object;
        Country country = address.getCountry();
        if (country.getISO2() == "FR") {
            // check address.getZipCode() structure for France (5 numbers)
            // check zipcode and city correlation (calling an external service?)
            return isValid;
        } else if (country.getISO2() == "GR") {
            // check address.getZipCode() structure for Greece
            // no zipcode / city correlation available at the moment
            return isValid;
        }
        // ...
    }
}

Gelişmiş adres doğrulama kuralları, adres nesnesinin dışında bırakıldı ve tarafından uygulandı MultiCountryAddressValidator. Nesne örneğine erişerek, sınıf düzeyindeki kısıtlamalar çok fazla esnekliğe sahiptir ve birden çok ilişkili özelliği doğrulayabilir. Burada sıralamanın denklemin dışında kaldığını unutmayın, bir sonraki yazıda ona geri döneceğiz.

Uzman grubu, çeşitli çoklu özellik destek yaklaşımlarını tartıştı: Sınıf düzeyinde kısıtlama yaklaşımının, bağımlılıkları içeren diğer özellik seviyesi yaklaşımlarına kıyasla hem yeterli basitlik hem de esneklik sağladığını düşünüyoruz. Görüşleriniz açığız.


17
Örnekte ConstraintValidator arabirimi ve @Constraint ek açıklaması ters çevrilmiştir. Ve geçerlidir () 2 parametre alır.
Guillaume Husta

1
TYPEve sırasıyla ve RUNTIMEile değiştirilmelidir . ElementType.TYPERetentionPolicy.RUNTIME
mark.monteiro

2
@ mark.monteiro Statik içe aktarmaları kullanabilirsiniz: import static java.lang.annotation.ElementType.*;veimport static java.lang.annotation.RetentionPolicy.*;
cassiomolin

2
Örneği Bean Validation ile çalışacak şekilde yeniden yazdım. Buraya bir göz atın .
cassiomolin

1
Ek açıklamanın parametreleri, şartname dahilinde doğru değildir, çünkü Cassio'nun bu cevap altında bahsettiği gibi bir mesaj, gruplar ve yük olması gerekir.
Peter S.

38

İle düzgün çalışması için Bean Doğrulama , Pascal Thivent en sağlanan örnek cevap şöyle yazılabilirdi:

@ValidAddress
public class Address {

    @NotNull
    @Size(max = 50)
    private String street1;

    @Size(max = 50)
    private String street2;

    @NotNull
    @Size(max = 10)
    private String zipCode;

    @NotNull
    @Size(max = 20)
    private String city;

    @Valid
    @NotNull
    private Country country;

    // Getters and setters
}
public class Country {

    @NotNull
    @Size(min = 2, max = 2)
    private String iso2;

    // Getters and setters
}
@Documented
@Target(TYPE)
@Retention(RUNTIME)
@Constraint(validatedBy = { MultiCountryAddressValidator.class })
public @interface ValidAddress {

    String message() default "{com.example.validation.ValidAddress.message}";

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

    Class<? extends Payload>[] payload() default {};
}
public class MultiCountryAddressValidator 
       implements ConstraintValidator<ValidAddress, Address> {

    public void initialize(ValidAddress constraintAnnotation) {

    }

    @Override
    public boolean isValid(Address address, 
                           ConstraintValidatorContext constraintValidatorContext) {

        Country country = address.getCountry();
        if (country == null || country.getIso2() == null || address.getZipCode() == null) {
            return true;
        }

        switch (country.getIso2()) {
            case "FR":
                return // Check if address.getZipCode() is valid for France
            case "GR":
                return // Check if address.getZipCode() is valid for Greece
            default:
                return true;
        }
    }
}

Bir CDI bean için WebSphere dinlendirici projesinde özel doğrulayıcı nasıl önyüklenir veya çalıştırılır? Özel kısıtlama dışında her şeyi yazdım veya çalıştırılmıyor
BalaajiChander

Benzer bir doğrulamayla sıkışıp kaldım, ancak benimki isoA2CodeDB Countrytablosunda saklanıyor . Buradan DB araması yapmak iyi bir fikir mi? Ayrıca, doğrulamadan sonra onları bağlamak istiyorum çünkü Address belongs_toa Countryve addressgirişin countrytablonun yabancı anahtarına sahip olmasını istiyorum . Ülkeyi adrese nasıl bağlarım?
krozaine

Yanlış bir nesneye bir tür doğrulama ek açıklaması ayarladığınızda, Bean Doğrulama çerçevesi tarafından bir istisna atılacağını unutmayın. Örneğin, @ValidAddressek açıklamayı Ülke nesnesine ayarlarsanız, bir No validator could be found for constraint 'com.example.validation.ValidAddress' validating type 'com.example.Country'istisna elde edersiniz .
Jacob van Lingen

12

Bean Doğrulama şartnamesine uymak istediğinizde, özel bir sınıf seviyesi doğrulayıcı gitmeniz gereken yoldur, örnek burada .

Hazırda Bekletme Doğrulayıcısı özelliğini kullanmaktan memnunsanız , Validator- 4.1.0.Final'den beri sağlanan @ ScriptAssert'i kullanabilirsiniz . JavaDoc'tan Exceprt:

Komut dosyası ifadeleri , sınıf yolunda JSR 223 ("JavaTM Platformu için Komut Dosyası") uyumlu bir motorun bulunabileceği herhangi bir komut dosyası veya ifade dilinde yazılabilir .

Misal:

@ScriptAssert(lang = "javascript", script = "_this.value1 != null || _this != value2)")
public class MyBean {
  private String value1;
  private String value2;
}

Evet ve Java 6, Rhino (JavaScript motoru) içerir, böylece ekstra bağımlılıklar eklemeden ifade dili olarak JavaScript'i kullanabilirsiniz.

3
Burada hazırda Doğrulayıcı 5.1.1.Final ile böyle bir doğrulama oluşturma örneğidir
Ivan Hristov
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.