【hibernate validator】(六)创建自定义约束

首发博客地址

https://blog.zysicyj.top/

一、创建一个简单的约束

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<CheckCaseString{
    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<CheckCaseString{
    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<MyFutureInstant{
    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<ZipCodeString{
    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 )
                .getConstraintValidatorPayloadString.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<ValidPassengerCountCar
{
    @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<ValidPassengerCountCar
{
    @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<ConsistentDateParametersObject[]> 
{
    @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[0instanceof Date ) || !( value[1instanceof 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 多平台发布

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员朱永胜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值