두 개 이상의 필드를 조합하여 유효성을 검사하려면 어떻게해야합니까?


92

내 모델의 유효성을 검사하기 위해 JPA 2.0 / Hibernate 유효성 검사를 사용하고 있습니다. 이제 두 필드의 조합을 확인해야하는 상황이 있습니다.

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

이 모델은되어 무효 두 경우 getValue1()와는 getValue2()이다 null유효한 그렇지.

JPA 2.0 / Hibernate로 이런 종류의 유효성 검사를 어떻게 수행 할 수 있습니까? 간단한 @NotNull주석을 사용하면 유효성 검사를 통과하려면 두 getter가 모두 null이 아니어야합니다.


답변:


102

여러 속성 유효성 검사의 경우 클래스 수준 제약 조건을 사용해야합니다. 에서 콩 검증 슬쩍 부분 II : 사용자 정의 제약 조건 :

### 클래스 수준 제약

여러분 중 일부는 여러 속성에 걸쳐 제약 조건을 적용하거나 여러 속성에 의존하는 제약 조건을 표현하는 기능에 대해 우려를 표명했습니다. 고전적인 예는 주소 유효성 검사입니다. 주소에는 복잡한 규칙이 있습니다.

  • 거리 이름은 다소 표준 적이며 길이 제한이 있어야합니다.
  • 우편 번호 구조는 전적으로 국가에 따라 다릅니다.
  • 도시는 종종 우편 번호와 연관 될 수 있으며 일부 오류 검사를 수행 할 수 있습니다 (유효성 검사 서비스에 액세스 할 수있는 경우).
  • 이러한 상호 의존성 때문에 간단한 자산 수준 제약이 청구서에 적합합니다.

Bean Validation 사양에서 제공하는 솔루션은 두 가지입니다.

  • 그룹 및 그룹 시퀀스를 사용하여 다른 제약 집합보다 먼저 적용되도록 제약 집합을 강제하는 기능을 제공합니다. 이 주제는 다음 블로그 항목에서 다룹니다.
  • 클래스 레벨 제약을 정의 할 수 있습니다.

클래스 수준 제약은 속성이 아닌 클래스에 적용되는 일반 제약 (주석 / 구현 듀오)입니다. 다르게 말하면 클래스 수준 제약 조건은 .NET에서 속성 값이 아닌 객체 인스턴스를 수신합니다 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;
        }
        // ...
    }
}

고급 주소 유효성 검사 규칙은 주소 개체에서 제외되었으며에 의해 구현되었습니다 MultiCountryAddressValidator. 객체 인스턴스에 액세스함으로써 클래스 수준 제약은 많은 유연성을 가지며 여러 상관 속성을 검증 할 수 있습니다. 순서는 여기 방정식에서 제외되며 다음 게시물에서 다시 설명하겠습니다.

전문가 그룹은 다양한 속성 지원 접근 방식을 논의했습니다. 클래스 수준 제약 방식은 종속성을 포함하는 다른 속성 수준 접근 방식에 비해 충분한 단순성과 유연성을 모두 제공한다고 생각합니다. 귀하의 의견을 환영합니다.


17
ConstraintValidator 인터페이스와 @Constraint 어노테이션이 예제에서 반전되었습니다. 그리고 valid ()는 2 개의 매개 변수를 취합니다.
Guillaume Husta 2014

1
TYPE그리고 RUNTIME교체되어야 ElementType.TYPE하고 RetentionPolicy.RUNTIME각각.
mark.monteiro

2
@ mark.monteiro 당신은 고정 수입을 사용할 수 있습니다 import static java.lang.annotation.ElementType.*;import static java.lang.annotation.RetentionPolicy.*;
cassiomolin

2
Bean Validation과 함께 작동하도록 예제를 다시 작성했습니다. 봐 가지고 여기를 .
cassiomolin

1
이 답변에서 Cassio가 언급 한 것과 같은 메시지, 그룹 및 페이로드가 있어야하기 때문에 주석의 매개 변수가 사양에 맞지 않습니다.
Peter S.

38

Bean Validation 에서 제대로 작동하려면 Pascal Thivent의 답변에 제공된 예제 를 다음과 같이 다시 작성할 수 있습니다.

@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;
        }
    }
}

CDI Bean에 대한 WebSphere Restful 프로젝트에서 사용자 정의 유효성 검증기를 부트 스트랩하거나 호출하는 방법은 무엇입니까? 나는 모든을 작성했지만 사용자 정의 제약은 작동하지 않거나 호출
BalaajiChander

비슷한 유효성 검사가 붙어 있지만 isoA2CodeDB Country테이블에 저장됩니다 . 여기에서 DB 호출을하는 것이 좋은 생각입니까? 또한 Address belongs_toa Country및 I address항목에 country테이블의 외래 키 가 있기를 원하기 때문에 유효성 검사 후 연결하고 싶습니다 . 국가를 주소에 연결하려면 어떻게해야합니까?
krozaine

잘못된 객체에 유형 유효성 검사 주석을 설정하면 Bean 유효성 검사 프레임 워크에서 예외가 발생합니다. 예를 들어 @ValidAddressCountry 개체에 주석을 설정하면 No validator could be found for constraint 'com.example.validation.ValidAddress' validating type 'com.example.Country'예외 가 발생합니다.
Jacob van Lingen

12

사용자 정의 클래스 수준 유효성 검사기는 Bean 유효성 검사 사양을 유지하려는 경우 사용할 수있는 방법입니다 (예 : 여기). 입니다.

Hibernate Validator 기능을 사용하고 싶다면 Validator-4.1.0.Final 이후 제공되는 @ScriptAssert를 사용할 수 있습니다 . JavaDoc에서 Exceprt :

스크립트 표현식은 모든 스크립팅 또는 표현식 언어로 작성 될 수 있으며, JSR 223 ( "JavaTM 플랫폼 용 스크립팅") 호환 엔진은 클래스 경로에서 찾을 수 있습니다.

예:

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

예, Java 6에는 Rhino (JavaScript 엔진)가 포함되어 있으므로 추가 종속성을 추가하지 않고도 JavaScript를 표현식 언어로 사용할 수 있습니다.

3
여기에 최대 절전 모드 검사기 5.1.1.Final와 함께 하나의 유효성 검사를 만드는 방법의 예입니다
이반 흐리스토프
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.