[java] 두 개 이상의 필드를 조합하여 유효성을 검사하려면 어떻게해야합니까?
내 모델의 유효성을 검사하기 위해 JPA 2.0 / Hibernate 유효성 검사를 사용하고 있습니다. 이제 두 필드의 조합을 확인해야하는 상황이 있습니다.
public class MyModel {
public Integer getValue1() {
//...
}
public String getValue2() {
//...
}
}
이 모델은되어 무효 두 경우 getValue1()
와는 getValue2()
이다 null
유효한 그렇지.
JPA 2.0 / Hibernate로 이런 종류의 유효성 검사를 어떻게 수행 할 수 있습니까? 간단한 @NotNull
주석을 사용하면 유효성 검사를 통과하려면 두 getter가 모두 null이 아니어야합니다.
답변
여러 속성 유효성 검사의 경우 클래스 수준 제약 조건을 사용해야합니다. 에서
콩 검증 슬쩍 부분 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
. 객체 인스턴스에 액세스함으로써 클래스 수준 제약은 많은 유연성을 가지며 여러 상관 속성을 검증 할 수 있습니다. 순서는 여기 방정식에서 제외되며 다음 게시물에서 다시 설명하겠습니다.전문가 그룹은 다양한 속성 지원 접근 방식을 논의했습니다. 클래스 수준 제약 방식은 종속성을 포함하는 다른 속성 수준 접근 방식에 비해 충분한 단순성과 유연성을 모두 제공한다고 생각합니다. 귀하의 의견을 환영합니다.
답변
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;
}
}
}
답변
사용자 정의 클래스 수준 유효성 검사기는 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;
}