前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >【hibernate validator】(六)创建自定义约束

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

作者头像
程序员朱永胜
发布于 2023-08-27 07:36:45
发布于 2023-08-27 07:36:45
20000
代码可运行
举报
运行总次数:0
代码可运行

首发博客地址

https://blog.zysicyj.top/

一、创建一个简单的约束

1. 约束注释

  • 枚举表示大小写
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public enum CaseMode {
    UPPER,
    LOWER;
}
  • 定义@CheckCase约束
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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();
    }
}
  • 测试一下吧
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Severity {
    public interface Info extends Payload {
    }
    public interface Error extends Payload {
    }
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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. 约束验证器

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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() );
        }
    }
}
  • 自定义错误消息
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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(对原版进行扩展)
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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初始化期间定义约束验证器有效载荷

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
        .configure()
        .constraintValidatorPayload( "US" )
        .buildValidatorFactory();
Validator validator = validatorFactory.getValidator();
  • 使用Validator上下文定义约束验证器有效载荷
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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
  • 在约束验证器中使用约束验证器有效载荷
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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. 错误讯息

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
org.hibernate.validator.referenceguide.chapter06.CheckCase.message = 案例模式必须为{value}

4. 使用约束

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 ...
}
  • 使用约束验证对象
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//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() );

二、类级别约束

  • 实现一个类级别约束
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@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 { };
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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();
    }
}

自定义属性路径

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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;
    }
}

三、交叉级别约束

  • 交叉级别约束
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@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 { };
}
  • 通用和交叉级别约束
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@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] );
    }
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@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;
}
  • 指定通用和交叉参数约束的目标
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@ScriptAssert(script = "arg1.size() <= arg0", validationAppliesTo = ConstraintTarget.PARAMETERS)
public Car buildCar(int seatCount, List<Passenger> passengers) {
    //...
    return null;
}

四、约束构成

  • 创建一个合成约束 @ValidLicensePlate
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@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
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Car {
    @ValidLicensePlate
    private String licensePlate;
    //...
}
  • 使用@ReportAsSingleViolation
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@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 多平台发布

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-08-26,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Python函数用法
python中函数的参数有位置参数、默认参数、可变参数、命名关键字参数和关键字参数,这个顺序也是定 义函数时的必须顺序。
星陨1357
2023/03/14
4190
python3--函数进阶
TypeError: func() missing 4 required keyword-only arguments: 'a', 'b', 'c', and 'd'
py3study
2018/08/02
5040
2.Python函数的进阶
昨天我们从形参角度,讲了两种参数,一个是位置参数,位置参数主要是实参与形参从左至右一一对应,一个是默认值参数,默认值参数,如果实参不传参,则形参使用默认参数。那么无论是位置参数,还是默认参数,函数调用时传入多少实参,我必须写等数量的形参去对应接收, 如果不这样,那么就会报错:
changxin7
2019/09/10
3040
2.Python函数的进阶
Python 变量作用域与函数
一个程序的所有的变量并不是在哪个位置都可以访问的,访问权限决定于这个变量是在哪里赋值的,变量的作用域决定了在哪一部分程序你可以访问哪个特定的变量名称,两种最基本的变量作用域,第一种是局部变量,第二种是全局变量.定义在函数内部的变量拥有一个局部作用域,定义在函数外的拥有全局作用域,而局部变量只能在其被声明的函数内部访问,全局变量则可以在整个程序范围内访问.
微软技术分享
2022/12/28
2.4K0
【Python】笔记第三部分:函数
对于 pycharm 而言,调试时使用快捷键 F8 step over 是不会进入到函数中的,F7 step into 是会进入的。换句话说,F7 会进入到代码的下一层,F8 会跳转到当前层的下一行。
杨丝儿
2022/02/17
3510
【Python】笔记第三部分:函数
day 10 函数的进阶
位置  *args 默认值  **kwargs   以后写参数,可以随意的进行搭配
py3study
2020/01/20
2970
Python之函数
简介: 定义函数: def xx():     print("xxx") 执行函数 xx() 函数返回值: def emile(): print("发邮件") return '123' r = emile() print(r) 结果: 发邮件 123 上述中,return为返回值,返回给r。return想返回什么就返回什么,如果没有写返回值,就返回None,就是空。 def emile(): if True: return True else:
用户1173509
2018/01/17
7900
Python之函数
python入门到放弃-函数专题
一、函数的定义 函数是对代码块和功能的封装和定义 #函数的语法:def是define的意思,定义 最基本的语法: def 函数名(): 函数体    函数名() #调用函数 带有参数的语法 def 函数名(形参列表): 函数体(代码块,return) 函数名(实参列表) :调用 #例子:函数执行过程 # def wan(): #定义函数 # print("今天一起去玩") # print("去哪里玩呢") # print("我不知道"
老油条IT记
2020/03/20
1.8K0
3.(补充)python中的lambda
从上面的例子可以看出,lambda函数不需要手动定义返回值,表达式的结果,就会直接作为返回值返回。
py3study
2020/01/10
3210
Python3 | 练气期,函数创建、参数传递、作用域!
描述:上一章,我们学习了Python3编程中最基本而得流程控制语句,相信大家在作者的实践下也已经掌握了相关关键字了吧,这一章我们一起学习Python3编程入门中函数定义、函数调用、函数参数(传递、类型),匿名函数、递归函数。内嵌函数和闭包、装饰器函数,以及命名空间作用域的讲解,它也是Python编程中最基础且常用的部分,所以说也是需要我们掌握的。
全栈工程师修炼指南
2024/07/29
610
Python3 | 练气期,函数创建、参数传递、作用域!
python学习
delete = users.pop(0)可以将删除的数据存储在delete中,而del仅仅是删除
sugarbeet
2022/09/26
8560
Python函数
概念:在一个完整的项目中,某些功能会被反复使用。那么会将某段代码封装成函数,当我们要使用功能的时候直接调用函数即可
星哥玩云
2022/09/08
4160
python3--函数初识
函数能提高应用的模块性,和代码的重复利用率。已经知道python提高了许多内建函数,比如print(),len()等。但你也可以自己创建函数,这被叫做用户自定义函数。
py3study
2018/08/02
5240
Python函数 & 变量
注意:因为函数体相对比较独立,函数定义的上方,应该和其他代码 (包括注释) 保留两个空行.
鱼多多
2023/12/27
2370
python中作用域与函数嵌套
实际上我们在定义函数的时候,如果省略了星号,那么在调用函数的时候必须要省略星号,除非我们拆解后的参数个数刚好相等。
刘金玉编程
2019/07/31
7190
Python升级之路( Lv5 ) 函数
第一章 Python 入门 第二章 Python基本概念 第三章 序列 第四章 控制语句 第五章 函数
时间静止不是简史
2022/12/02
1.2K0
Python升级之路( Lv5 ) 函数
Python入门之函数的嵌套/名称空间/作用域/函数对象/闭包函数
本篇目录:     一、函数嵌套     二、函数名称空间与作用域     三、函数对象     四、闭包函数 ============================================================================== 一、函数嵌套 1. 函数的嵌套调用 函数内又调用了其他函数(函数平级) def max(x,y): return x if x > y else y def max4(a,b,c,d): res1=max(a,b)
Jetpropelledsnake21
2018/05/02
1.3K0
Python入门之函数的嵌套/名称空间/作用域/函数对象/闭包函数
2022年最新Python大数据之Python基础【六】函数与变量
文章目录 1、公共方法 2、公共函数 3、推导式 4、函数介绍 5、函数参数 6、函数返回值 7、函数的嵌套 8、局部变量和全局变量 9、gloal 10、函数参数进阶 1、公共方法 + 加法运算适用于所有的基础数据类型(int float bool) 加法运算所有两侧要是同种数据类型 加法运算再容器类型中是拼接的意思,不是相加计算值 # +法运算,都可以用于哪些数据类型之间 # int float bool 肯定可以用于加法运算,不再赘述 print(1 + 12.3) # 13.3 # st
Maynor
2022/09/26
1.2K0
【Python 初级函数详解】—— 参数沙漠与作用域丛林的求生指南
我们知道数学中的函数,我们输入一个数,在通过对应的映射关系得到另一个数,如下图给出了两个简单的数学函数:
换一颗红豆
2025/03/03
560
【Python 初级函数详解】—— 参数沙漠与作用域丛林的求生指南
Python 变量的作用域
变量可以使用的范围,程序的变量并不是哪个位置都可以访问的,访问的权限决定于变量时在哪里定义的
星哥玩云
2022/09/08
7760
Python 变量的作用域
相关推荐
Python函数用法
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验