首发博客地址
一、创建一个简单的约束
1. 约束注释
- 枚举表示大小写
public enum CaseMode { UPPER, LOWER; }
- 定义@CheckCase约束
import static java.lang.annotation.ElementType.ANNOTATION_TYPE; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.ElementType.TYPE_USE; import static java.lang.annotation.RetentionPolicy.RUNTIME; @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE, TYPE_USE }) @Retention(RUNTIME) @Constraint(validatedBy = CheckCaseValidator.class) @Documented @Repeatable(List.class) public @interface CheckCase { String message() default "{org.hibernate.validator.referenceguide.chapter06.CheckCase." + "message}"; Class<?>[] groups() default { }; Class<? extends Payload>[] payload() default { }; CaseMode value(); @Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE }) @Retention(RUNTIME) @Documented @interface List { CheckCase[] value(); } }
- 测试一下吧
public class Severity { public interface Info extends Payload { } public interface Error extends Payload { } }
public class ContactDetails { @NotNull(message = "Name is mandatory", payload = Severity.Error.class) private String name; @NotNull(message = "Phone number not specified, but not mandatory", payload = Severity.Info.class) private String phoneNumber; // ... }
用法
@Target 定义约束所支持的目标元素类型
@Retention(RUNTIME):指定此类型的注释将在运行时通过反射方式提供
@Constraint(validatedBy = CheckCaseValidator.class):将注释类型标记为约束注释,并指定用于验证元素的验证器
@CheckCase。如果可以在几种数据类型上使用约束,则可以指定几个验证器,每种数据类型一个。
@Repeatable(List.class):表示注释可以在同一位置重复多次,通常使用不同的配置
2. 约束验证器
public class CheckCaseValidator implements ConstraintValidator<CheckCase, String> { private CaseMode caseMode; @Override public void initialize(CheckCase constraintAnnotation) { this.caseMode = constraintAnnotation.value(); } @Override public boolean isValid(String object, ConstraintValidatorContext constraintContext) { if ( object == null ) { return true; } if ( caseMode == CaseMode.UPPER ) { return object.equals( object.toUpperCase() ); } else { return object.equals( object.toLowerCase() ); } } }
- 自定义错误消息
public class CheckCaseValidator implements ConstraintValidator<CheckCase, String> { private CaseMode caseMode; @Override public void initialize(CheckCase constraintAnnotation) { this.caseMode = constraintAnnotation.value(); } @Override public boolean isValid(String object, ConstraintValidatorContext constraintContext) { if ( object == null ) { return true; } boolean isValid; if ( caseMode == CaseMode.UPPER ) { isValid = object.equals( object.toUpperCase() ); } else { isValid = object.equals( object.toLowerCase() ); } if ( !isValid ) { constraintContext.disableDefaultConstraintViolation(); constraintContext.buildConstraintViolationWithTemplate( "{org.hibernate.validator.referenceguide.chapter06." + "constraintvalidatorcontext.CheckCase.message}" ) .addConstraintViolation(); } return isValid; } }
- HibernateConstraintValidator(对原版进行扩展)
public class MyFutureValidator implements HibernateConstraintValidator<MyFuture, Instant> { private Clock clock; private boolean orPresent; @Override public void initialize(ConstraintDescriptor<MyFuture> constraintDescriptor, HibernateConstraintValidatorInitializationContext initializationContext) { this.orPresent = constraintDescriptor.getAnnotation().orPresent(); this.clock = initializationContext.getClockProvider().getClock(); } @Override public boolean isValid(Instant instant, ConstraintValidatorContext constraintContext) { //... return false; } }
将有效负载传递给约束验证器
- 在ValidatorFactory初始化期间定义约束验证器有效载荷
ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) .configure() .constraintValidatorPayload( "US" ) .buildValidatorFactory(); Validator validator = validatorFactory.getValidator();
- 使用Validator上下文定义约束验证器有效载荷
HibernateValidatorFactory hibernateValidatorFactory = Validation.byDefaultProvider() .configure() .buildValidatorFactory() .unwrap( HibernateValidatorFactory.class ); Validator validator = hibernateValidatorFactory.usingContext() .constraintValidatorPayload( "US" ) .getValidator(); // [...] US specific validation checks validator = hibernateValidatorFactory.usingContext() .constraintValidatorPayload( "FR" ) .getValidator(); // [...] France specific validation checks
- 在约束验证器中使用约束验证器有效载荷
public class ZipCodeValidator implements ConstraintValidator<ZipCode, String> { public String countryCode; @Override public boolean isValid(String object, ConstraintValidatorContext constraintContext) { if ( object == null ) { return true; } boolean isValid = false; String countryCode = constraintContext .unwrap( HibernateConstraintValidatorContext.class ) .getConstraintValidatorPayload( String.class ); if ( "US".equals( countryCode ) ) { // checks specific to the United States } else if ( "FR".equals( countryCode ) ) { // checks specific to France } else { // ... } return isValid; } }
3. 错误讯息
org.hibernate.validator.referenceguide.chapter06.CheckCase.message = 案例模式必须为{value}。
4. 使用约束
public class Car { @NotNull private String manufacturer; @NotNull @Size(min = 2, max = 14) @CheckCase(CaseMode.UPPER) private String licensePlate; @Min(2) private int seatCount; public Car(String manufacturer, String licencePlate, int seatCount) { this.manufacturer = manufacturer; this.licensePlate = licencePlate; this.seatCount = seatCount; } //getters and setters ... }
- 使用约束验证对象
//invalid license plate Car car = new Car( "Morris", "dd-ab-123", 4 ); Set<ConstraintViolation<Car>> constraintViolations = validator.validate( car ); assertEquals( 1, constraintViolations.size() ); assertEquals( "Case mode must be UPPER.", constraintViolations.iterator().next().getMessage() ); //valid license plate car = new Car( "Morris", "DD-AB-123", 4 ); constraintViolations = validator.validate( car ); assertEquals( 0, constraintViolations.size() );
二、类级别约束
- 实现一个类级别约束
@Target({ TYPE, ANNOTATION_TYPE }) @Retention(RUNTIME) @Constraint(validatedBy = { ValidPassengerCountValidator.class }) @Documented public @interface ValidPassengerCount { String message() default "{org.hibernate.validator.referenceguide.chapter06.classlevel." + "ValidPassengerCount.message}"; Class<?>[] groups() default { }; Class<? extends Payload>[] payload() default { }; }
public class ValidPassengerCountValidator implements ConstraintValidator<ValidPassengerCount, Car> { @Override public void initialize(ValidPassengerCount constraintAnnotation) { } @Override public boolean isValid(Car car, ConstraintValidatorContext context) { if ( car == null ) { return true; } return car.getPassengers().size() <= car.getSeatCount(); } }
自定义属性路径
public class ValidPassengerCountValidator implements ConstraintValidator<ValidPassengerCount, Car> { @Override public void initialize(ValidPassengerCount constraintAnnotation) { } @Override public boolean isValid(Car car, ConstraintValidatorContext constraintValidatorContext) { if ( car == null ) { return true; } boolean isValid = car.getPassengers().size() <= car.getSeatCount(); if ( !isValid ) { constraintValidatorContext.disableDefaultConstraintViolation(); constraintValidatorContext .buildConstraintViolationWithTemplate( "{my.custom.template}" ) .addPropertyNode( "passengers" ).addConstraintViolation(); } return isValid; } }
三、交叉级别约束
- 交叉级别约束
@Constraint(validatedBy = ConsistentDateParametersValidator.class) @Target({ METHOD, CONSTRUCTOR, ANNOTATION_TYPE }) @Retention(RUNTIME) @Documented public @interface ConsistentDateParameters { String message() default "{org.hibernate.validator.referenceguide.chapter04." + "crossparameter.ConsistentDateParameters.message}"; Class<?>[] groups() default { }; Class<? extends Payload>[] payload() default { }; }
- 通用和交叉级别约束
@SupportedValidationTarget(ValidationTarget.PARAMETERS) public class ConsistentDateParametersValidator implements ConstraintValidator<ConsistentDateParameters, Object[]> { @Override public void initialize(ConsistentDateParameters constraintAnnotation) { } @Override public boolean isValid(Object[] value, ConstraintValidatorContext context) { if ( value.length != 2 ) { throw new IllegalArgumentException( "Illegal method signature" ); } //leave null-checking to @NotNull on individual parameters if ( value[0] == null || value[1] == null ) { return true; } if ( !( value[0] instanceof Date ) || !( value[1] instanceof Date ) ) { throw new IllegalArgumentException( "Illegal method signature, expected two " + "parameters of type Date." ); } return ( (Date) value[0] ).before( (Date) value[1] ); } }
@Constraint(validatedBy = { ScriptAssertObjectValidator.class, ScriptAssertParametersValidator.class }) @Target({ TYPE, FIELD, PARAMETER, METHOD, CONSTRUCTOR, ANNOTATION_TYPE }) @Retention(RUNTIME) @Documented public @interface ScriptAssert { String message() default "{org.hibernate.validator.referenceguide.chapter04." + "crossparameter.ScriptAssert.message}"; Class<?>[] groups() default { }; Class<? extends Payload>[] payload() default { }; String script(); ConstraintTarget validationAppliesTo() default ConstraintTarget.IMPLICIT; }
- 指定通用和交叉参数约束的目标
@ScriptAssert(script = "arg1.size() <= arg0", validationAppliesTo = ConstraintTarget.PARAMETERS) public Car buildCar(int seatCount, List<Passenger> passengers) { //... return null; }
四、约束构成
- 创建一个合成约束 @ValidLicensePlate
@NotNull @Size(min = 2, max = 14) @CheckCase(CaseMode.UPPER) @Target({ METHOD, FIELD, ANNOTATION_TYPE, TYPE_USE }) @Retention(RUNTIME) @Constraint(validatedBy = { }) @Documented public @interface ValidLicensePlate { String message() default "{org.hibernate.validator.referenceguide.chapter06." + "constraintcomposition.ValidLicensePlate.message}"; Class<?>[] groups() default { }; Class<? extends Payload>[] payload() default { }; }
- 组合约束的应用 ValidLicensePlate
public class Car { @ValidLicensePlate private String licensePlate; //... }
- 使用@ReportAsSingleViolation
@ReportAsSingleViolation public @interface ValidLicensePlate { String message() default "{org.hibernate.validator.referenceguide.chapter06." + "constraintcomposition.reportassingle.ValidLicensePlate.message}"; Class<?>[] groups() default { }; Class<? extends Payload>[] payload() default { }; } ```ayload() default { }; }
本文由 mdnice 多平台发布