【hibernate validator】(二)声明和验证Bean约束

news/2024/7/8 4:46:06 标签: 后端

首发博客地址

https://blog.zysicyj.top/

一、声明bean约束

1. 字段级别约束

  1. 不支持静态类型字段
  2. 验证引擎直接访问实例变量,不会调用属性的访问器
  3. 在验证字节码增强的对象时,应适用属性级别约束,因为字节码增库无法通过反射确定字段访问
package org.hibernate.validator.referenceguide.chapter02.fieldlevel;
public class Car {
    @NotNull
    private String manufacturer;
    @AssertTrue
    private boolean isRegistered;
    public Car(String manufacturer, boolean isRegistered) {
        this.manufacturer = manufacturer;
        this.isRegistered = isRegistered;
    }
    //getters and setters...
}

2. 属性级别约束

  1. 必须注释getter而不是setter,这样可以限制没有设置方法的只读属性
  2. 该级别将使用属性访问策略来访问验证的值,即验证引擎通过属性访问器来访问数据
  3. 不要字段和getter都加校验,这样会导致校验两次
package org.hibernate.validator.referenceguide.chapter02.propertylevel;
public class Car {
    private String manufacturer;
    private boolean isRegistered;
    public Car(String manufacturer, boolean isRegistered) {
        this.manufacturer = manufacturer;
        this.isRegistered = isRegistered;
    }
    @NotNull
    public String getManufacturer() {
        return manufacturer;
    }
    public void setManufacturer(String manufacturer) {
        this.manufacturer = manufacturer;
    }
    @AssertTrue
    public boolean isRegistered() {
        return isRegistered;
    }
    public void setRegistered(boolean isRegistered) {
        this.isRegistered = isRegistered;
    }
}
  1. 容器元素约束

3.1 Iterable

在该类型上加约束时,将会校验每个元素

package org.hibernate.validator.referenceguide.chapter02.containerelement.set;
import java.util.HashSet;
import java.util.Set;
public class Car {
    private Set<@ValidPart String> parts = new HashSet<>();
    public void addPart(String part) {
        parts.add( part );
    }
    //...
}
Car car = new Car();
car.addPart( "Wheel" );
car.addPart( null );
Set<ConstraintViolation<Car>> constraintViolations = validator.validate( car );
assertEquals( 1, constraintViolations.size() );
ConstraintViolation<Car> constraintViolation =
        constraintViolations.iterator().next();
assertEquals(
        "'null' is not a valid car part.",
        constraintViolation.getMessage()
);
assertEquals( "parts[].<iterable element>",
        constraintViolation.getPropertyPath().toString() );

3.2 List

也会校验每个元素

package org.hibernate.validator.referenceguide.chapter02.containerelement.list;
public class Car {
    private List<@ValidPart String> parts = new ArrayList<>();
    public void addPart(String part) {
        parts.add( part );
    }
    //...
}
Car car = new Car();
car.addPart( "Wheel" );
car.addPart( null );
Set<ConstraintViolation<Car>> constraintViolations = validator.validate( car );
assertEquals( 1, constraintViolations.size() );
ConstraintViolation<Car> constraintViolation =
        constraintViolations.iterator().next();
assertEquals(
        "'null' is not a valid car part.",
        constraintViolation.getMessage()
);
assertEquals( "parts[1].<list element>",
        constraintViolation.getPropertyPath().toString() );

3.3 Map

package org.hibernate.validator.referenceguide.chapter02.containerelement.map;
import java.util.HashMap;
import java.util.Map;
import javax.validation.constraints.NotNull;
public class Car {
    public enum FuelConsumption {
        CITY,
        HIGHWAY
    }
    private Map<@NotNull FuelConsumption, @MaxAllowedFuelConsumption Integer> fuelConsumption = new HashMap<>();
    public void setFuelConsumption(FuelConsumption consumption, int value) {
        fuelConsumption.put( consumption, value );
    }
    //...
}
Car car = new Car();
car.setFuelConsumption( Car.FuelConsumption.HIGHWAY, 20 );
Set<ConstraintViolation<Car>> constraintViolations = validator.validate( car );
assertEquals( 1, constraintViolations.size() );
ConstraintViolation<Car> constraintViolation =
        constraintViolations.iterator().next();
assertEquals(
        "20 is outside the max fuel consumption.",
        constraintViolation.getMessage()
);
assertEquals(
        "fuelConsumption[HIGHWAY].<map value>",
        constraintViolation.getPropertyPath().toString()
);
Car car = new Car();
car.setFuelConsumption( null5 );
Set<ConstraintViolation<Car>> constraintViolations = validator.validate( car );
assertEquals( 1, constraintViolations.size() );
ConstraintViolation<Car> constraintViolation =
        constraintViolations.iterator().next();
assertEquals(
        "must not be null",
        constraintViolation.getMessage()
);
assertEquals(
        "fuelConsumption<K>[].<map key>",
        constraintViolation.getPropertyPath().toString()
);

3.4 Optional

package org.hibernate.validator.referenceguide.chapter02.containerelement.optional;
public class Car {
    private Optional<@MinTowingCapacity(1000) Integer> towingCapacity = Optional.empty();
    public void setTowingCapacity(Integer alias) {
        towingCapacity = Optional.of( alias );
    }
    //...
}
Car car = new Car();
car.setTowingCapacity( 100 );
Set<ConstraintViolation<Car>> constraintViolations = validator.validate( car );
assertEquals( 1, constraintViolations.size() );
ConstraintViolation<Car> constraintViolation = constraintViolations.iterator().next();
assertEquals(
        "Not enough towing capacity.",
        constraintViolation.getMessage()
);
assertEquals(
        "towingCapacity",
        constraintViolation.getPropertyPath().toString()
);

3.5 自定义容器

package org.hibernate.validator.referenceguide.chapter02.containerelement.custom;
public class Car {
    private GearBox<@MinTorque(100) Gear> gearBox;
    public void setGearBox(GearBox<Gear> gearBox) {
        this.gearBox = gearBox;
    }
    //...
}
package org.hibernate.validator.referenceguide.chapter02.containerelement.custom;
public class GearBox<T extends Gear{
    private final T gear;
    public GearBox(T gear) {
        this.gear = gear;
    }
    public Gear getGear() {
        return this.gear;
    }
}
package org.hibernate.validator.referenceguide.chapter02.containerelement.custom;
public class Gear {
    private final Integer torque;
    public Gear(Integer torque) {
        this.torque = torque;
    }
    public Integer getTorque() {
        return torque;
    }
    public static class AcmeGear extends Gear {
        public AcmeGear() {
            super60 );
        }
    }
}
package org.hibernate.validator.referenceguide.chapter02.containerelement.custom;
public class GearBoxValueExtractor implements ValueExtractor<GearBox<@ExtractedValue ?>> {
    @Override
    public void extractValues(GearBox<@ExtractedValue ?> originalValue, ValueExtractor.ValueReceiver receiver) {
        receiver.value( null, originalValue.getGear() );
    }
}
Car car = new Car();
car.setGearBox( new GearBox<>( new Gear.AcmeGear() ) );
Set<ConstraintViolation<Car>> constraintViolations = validator.validate( car );
assertEquals( 1, constraintViolations.size() );
ConstraintViolation<Car> constraintViolation =
        constraintViolations.iterator().next();
assertEquals(
        "Gear is not providing enough torque.",
        constraintViolation.getMessage()
);
assertEquals(
        "gearBox",
        constraintViolation.getPropertyPath().toString()
);

3.6 嵌套容器元素

package org.hibernate.validator.referenceguide.chapter02.containerelement.nested;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.validation.constraints.NotNull;
public class Car {
    private Map<@NotNull Part, List<@NotNull Manufacturer>> partManufacturers =
            new HashMap<>();
    //...
}

4. 类级别约束

  1. 在这种情况下,验证的对象不是单个属性而是完整的对象
  2. 适合依赖于对象的多个属性之间的相关性很高的场景
package org.hibernate.validator.referenceguide.chapter02.classlevel;
@ValidPassengerCount
public class Car {
    private int seatCount;
    private List<Person> passengers;
    //...
}

5. 约束继承

在一个类实现接口或扩展另一个类时,在超类上声明的所有约束注释都以与该类本身上指定的约束相同的方式约束

package org.hibernate.validator.referenceguide.chapter02.inheritance;
public class Car {
    private String manufacturer;
    @NotNull
    public String getManufacturer() {
        return manufacturer;
    }
    //...
}
package org.hibernate.validator.referenceguide.chapter02.inheritance;
public class RentalCar extends Car {
    private String rentalStation;
    @NotNull
    public String getRentalStation() {
        return rentalStation;
    }
    //...
}
  1. RentalCar 不仅会校验getRentalStation,而且会校验父类的getManufacturer
  2. 若继承换成接口,也是会校验超类的

6. 对象图

不仅支持单个对象校验,还支持级联验证

对象的级联校验

package org.hibernate.validator.referenceguide.chapter02.objectgraph;
public class Car {
    @NotNull
    @Valid
    private Person driver;
    //...
}
package org.hibernate.validator.referenceguide.chapter02.objectgraph;
public class Person {
    @NotNull
    private String name;
    //...
}

在校验Car的时候,会校验Person,因此若Car引用的Person的name为空,则会校验失败

容器的级联校验

package org.hibernate.validator.referenceguide.chapter02.objectgraph.containerelement;
public class Car {
    private List<@NotNull @Valid Person> passengers = new ArrayList<Person>();
    private Map<@Valid Part, List<@Valid Manufacturer>> partManufacturers = new HashMap<>();
    //...
}
package org.hibernate.validator.referenceguide.chapter02.objectgraph.containerelement;
public class Part {
    @NotNull
    private String name;
    //...
}
package org.hibernate.validator.referenceguide.chapter02.objectgraph.containerelement;
public class Manufacturer {
    @NotNull
    private String name;
    //...
}
  1. 校验Person的名字是否存在为null的
  2. 校验Part的名字是否存在为null的
  3. 校验所有的Manufacturer是否存在名字为null的

二、验证Bean约束

1. 获取验证器

2. 验证的三种方式

先来个车

class Car {
    @NotNull
    @Size(min = 5,max = 20)
    private String manufacturer;
    @AssertTrue
    private boolean isRegistered;
    public Car(String manufacturer, boolean isRegistered) {
        this.manufacturer = manufacturer;
        this.isRegistered = isRegistered;
    }
}

bean全部验证

验证单个属性

对属性的值进行验证

3. 约束违规

「内插的错误消息」

09:35:00.446 [main] INFO com.bm.validate.TestValidatorBean - 内插的错误消息:只能为true

非插补的错误消息

09:35:00.446 [main] INFO com.bm.validate.TestValidatorBean - 非插补的错误消息:{javax.validation.constraints.AssertTrue.message}

正在验证的根Bean

09:35:00.446 [main] INFO com.bm.validate.TestValidatorBean - 正在验证的根Bean:com.bm.validate.Car@7c83dc97

如果是bean约束,则将约束应用到bean实例;如果是属性约束,则是托管该约束的属性的bean实例

09:35:00.446 [main] INFO com.bm.validate.TestValidatorBean - 如果是bean约束,则将约束应用到bean实例;如果是属性约束,则是托管该约束的属性的bean实例:com.bm.validate.Car@7c83dc97

「bean验证器值的属性路径」

09:35:00.447 [main] INFO com.bm.validate.TestValidatorBean - 根bean验证器值的属性路径:isRegistered

**「报告约束失败的原数据」

09:35:00.447 [main] INFO com.bm.validate.TestValidatorBean - 报告约束失败的原数据:false

「告约束失败的元数据」

09:35:00.447 [main] INFO com.bm.validate.TestValidatorBean - 报告约束失败的元数据:ConstraintDescriptorImpl{annotation=j.v.c.AssertTrue, payloads=[], hasComposingConstraints=true, isReportAsSingleInvalidConstraint=false, elementType=FIELD, definedOn=DEFINED_LOCALLY, groups=[interface javax.validation.groups.Default], attributes={groups=[Ljava.lang.Class;@60015ef5, message={javax.validation.constraints.AssertTrue.message}, payload=[Ljava.lang.Class;@2f54a33d}, constraintType=GENERIC, valueUnwrapping=DEFAULT}

三、内置约束

@AssertFalse

检查带注释元素的属性为false

  • Boolean, boolean

@AssertTrue

检查带注释元素的属性为True

  • Boolean,boolean

@DecimalMax(value=, inclusive=)

  • inclusive为false,检查带注释的值是否小于指定的最大值。否则,该值是否小于等于指定的最大值
  • BigDecimal,BigInteger,CharSequence,byte,short,int,long,原始数据包装类,Number,javax.money.MonetaryAmount任意子类

@DecimalMin(value=, inclusive=)

  • inclusive为false,检查带注释的值是否大于指定的最小值。否则,该值是否大于等于指定的最小值
  • BigDecimal,BigInteger,CharSequence,byte,short,int,long,原始数据包装类,Number,javax.money.MonetaryAmount任意子类

@Digits(integer=, fraction=)

  • integer 指定整数位数限制,fraction指定小数位数限制
  • BigDecimal,BigInteger,CharSequence,byte,short,int,long,原始数据包装类,Number,javax.money.MonetaryAmount任意子类

@Email

  • 是否为有效的电子邮箱地址
  • regexp和flags参数指定正则规则,必须匹配的其它表达式
  • CharSequence

@Future

  • 检查是否是将来的日期
  • java.util.Date,java.util.Calendar,java.time.Instant,java.time.LocalDate,java.time.LocalDateTime,java.time.LocalTime,java.time.MonthDay,java.time.OffsetDateTime,java.time.OffsetTime,java.time.Year,java.time.YearMonth,java.time.ZonedDateTime,java.time.chrono.HijrahDate,java.time.chrono.JapaneseDate,java.time.chrono.MinguoDate,java.time.chrono.ThaiBuddhistDate; 如果类路径上有Joda Time日期/时间API ,则由HV额外支持:ReadablePartial和的任何实现ReadableInstant

@FutureOnPresent

  • 检查日期是先在还是将来
  • java.util.Date,java.util.Calendar,java.time.Instant,java.time.LocalDate,java.time.LocalDateTime,java.time.LocalTime,java.time.MonthDay,java.time.OffsetDateTime,java.time.OffsetTime,java.time.Year,java.time.YearMonth,java.time.ZonedDateTime,java.time.chrono.HijrahDate,java.time.chrono.JapaneseDate,java.time.chrono.MinguoDate,java.time.chrono.ThaiBuddhistDate; 如果类路径上有Joda Time日期/时间API ,则由HV额外支持:ReadablePartial和的任何实现ReadableInstant

@Max(value=)

  • 是否小于或等于该值
  • BigDecimal,BigInteger,byte,short,int,long和原始类型的相应的包装; HV额外支持:的任何子类型CharSequence(评估字符序列表示的数值),Number和的任何子类型javax.money.MonetaryAmount

@Min(value=)

  • 是否大于或等于该值
  • BigDecimal,BigInteger,byte,short,int,long和原始类型的相应的包装; HV额外支持:的任何子类型CharSequence(评估字符序列表示的数值),Number和的任何子类型javax.money.MonetaryAmount

@NotBlank

  • 指定字符不为null并且长度大于0
  • CharSequence

@NotEmpty

  • 指定字符不为null或为空(去除尾随空格)
  • CharSequence,Collection,Map和数组

@NotNull

  • 检查注释的值不为null
  • 所有类型均支持

@Negative

  • 检查元素是否严格为负,零被视为无效
  • BigDecimal,BigInteger,byte,short,int,long和原始类型的相应的包装; HV额外支持:的任何子类型CharSequence(评估字符序列表示的数值),Number和的任何子类型javax.money.MonetaryAmount

@NegativeOrZero

  • 检查元素是负数或0
  • BigDecimal,BigInteger,byte,short,int,long和原始类型的相应的包装; HV额外支持:的任何子类型CharSequence(评估字符序列表示的数值),Number和的任何子类型javax.money.MonetaryAmount

@Null

  • 检查注释的值是null
  • 所有类型均支持

@Past

  • 检查带注释的日期是否是过去的日期
  • java.util.Date,java.util.Calendar,java.time.Instant,java.time.LocalDate,java.time.LocalDateTime,java.time.LocalTime,java.time.MonthDay,java.time.OffsetDateTime,java.time.OffsetTime,java.time.Year,java.time.YearMonth,java.time.ZonedDateTime,java.time.chrono.HijrahDate,java.time.chrono.JapaneseDate,java.time.chrono.MinguoDate,java.time.chrono.ThaiBuddhistDate; 如果类路径上有Joda Time日期/时间API ,则由HV附加支持:ReadablePartial和的任何实现ReadableInstant

@PastOrPresent

  • 检查带注释的日期是过去还是现在
  • java.util.Date,java.util.Calendar,java.time.Instant,java.time.LocalDate,java.time.LocalDateTime,java.time.LocalTime,java.time.MonthDay,java.time.OffsetDateTime,java.time.OffsetTime,java.time.Year,java.time.YearMonth,java.time.ZonedDateTime,java.time.chrono.HijrahDate,java.time.chrono.JapaneseDate,java.time.chrono.MinguoDate,java.time.chrono.ThaiBuddhistDate; 如果类路径上有Joda Time日期/时间API ,则由HV附加支持:ReadablePartial和的任何实现ReadableInstant

@Pattern(regex=, flags=)

  • regex考虑给定标志,检查带注释的字符串是否与正则表达式匹配match
  • CharSequence

@Positive

  • 检查元素是否严格为正。零值被视为无效
  • BigDecimal,BigInteger,byte,short,int,long和原始类型的相应的包装; HV额外支持:的任何子类型CharSequence(评估字符序列表示的数值),Number和的任何子类型javax.money.MonetaryAmount

@PositiveOrZero

  • 检查元素是否严格为正或零
  • BigDecimal,BigInteger,byte,short,int,long和原始类型的相应的包装; HV额外支持:的任何子类型CharSequence(评估字符序列表示的数值),Number和的任何子类型javax.money.MonetaryAmount

@Size(min=, max=)

  • 检查带注释的元素的大小是否介于min和之间max(包括)
  • CharSequence,Collection,Map和数组

@CreditCardNumber(ignoreNonDigitCharacters=)

  • 检查带注释的字符序列是否通过了Luhn校验和测试
  • ignoreNonDigitCharacters允许忽略非数字字符。默认值为false。
  • CharSequence

@Currency(value=)

  • 检查带注释的货币单位javax.money.MonetaryAmount是否为指定货币单位的一部分。
  • javax.money.MonetaryAmount

@DurationMax(days=, hours=, minutes=, seconds=, millis=, nanos=, inclusive=)

  • 检查带注释的java.time.Duration元素不大于由注释参数构造的元素。如果将inclusiveflag设置为,则允许平等true
  • java.time.Duration

@DurationMin(days=, hours=, minutes=, seconds=, millis=, nanos=, inclusive=)

  • 检查带注释的java.time.Duration元素不少于由注释参数构造的元素。如果将inclusiveflag设置为,则允许平等true。
  • java.time.Duration

@EAN

  • 检查带注释的字符序列是有效的EAN条形码。类型决定条形码的类型
  • CharSequence

@ISBN

  • 检查带注释的字符序列是有效的ISBN
  • CharSequence

@Length(min=, max=)

  • 验证该注释字符序列是间min和max包含
  • CharSequence

@Range(min=, max=)

  • 检查带注释的值是否介于(包括)指定的最小值和最大值之间
  • BigDecimal,BigInteger,CharSequence,byte,short,int,long和原始类型的相应的包装

@UniqueElements

  • 检查带注释的集合仅包含唯一元素。使用该equals()方法确定相等性。默认消息不包括重复元素的列表,但是您可以通过覆盖消息并使用{duplicates}message参数来包括它。重复元素的列表也包含在约束违反的动态有效负载中。
  • Collection��负载中。
  • Collection

本文由 mdnice 多平台发布


http://www.niftyadmin.cn/n/4972839.html

相关文章

时序分解 | MATLAB实现基于SVD奇异值分解的信号分解分量可视化

时序分解 | MATLAB实现基于SVD奇异值分解的信号分解分量可视化 目录 时序分解 | MATLAB实现基于SVD奇异值分解的信号分解分量可视化效果一览基本介绍程序设计参考资料 效果一览 基本介绍 SVD分解重构算法&#xff0c;MATLAB程序&#xff0c;奇异值分解 (Singular Value Decompo…

WPF怎么实现文件拖放功能winform怎么实现拖拽功能

WPF怎么实现文件拖放功能winform怎么实现文件拖拽功能&#xff0c;在管理员模式下wpf winform怎么实现文件的拖拽功能 WPF实现文件拖放功能&#xff0c;正常情况并没有什么问题&#xff0c;但是如果你的程序使用管理员身份启动&#xff0c;你就会发现文件拖放功能就会失效。同…

自定义date工具类 DateUtils.java

自定义date工具类 DateUtils.java 简介 Date日期类型的工具类。 api 日期格式化 format(Date date)&#xff1b;日期格式化 format(Date date, String pattern)&#xff1b;计算距离现在多久&#xff0c;非精确 getTimeBefore(Date date);计算距离现在多久&#xff0c;精确…

MTK6833_MT6833核心板_天玑700安卓5G核心板规格性能介绍

MTK6833安卓核心板采用台积电 7nm 制程的5G SoC&#xff0c;2*Cortex-A766*Cortex-A55架构&#xff0c;搭载Android12.0操作系统&#xff0c;主频最高达2.2GHz 。内置 5G 双载波聚合技术&#xff08;2CC&#xff09;及双 5G SIM 卡功能&#xff0c;实现优异的功耗表现及实时连网…

Matlab图像处理运算方法-点运算

点运算 点运算又称为对比度增强、对比度拉伸或灰度变换&#xff0c;是一种通过图像中的每一个像素值&#xff08;即像素点上的灰度值&#xff09;进行运算的图像处理方式。它将输入图像映射为输出图像&#xff0c;输出图像每个像素点的灰度值仅由对应的输入像素点的灰度值决定…

InCopy软件安装包分享(附安装教程)

目录 一、软件简介 二、软件下载 一、软件简介 InCopy是一款由Adobe公司开发的文字处理软件&#xff0c;它被广泛应用于出版行业和印刷行业&#xff0c;用于编辑和排版文字内容。InCopy与Adobe的另一款软件InDesign紧密集成&#xff0c;共同构成了专业的排版工作流程。 以下…

【MySQL系列】表的内连接和外连接学习

「前言」文章内容大致是对MySQL表的内连接和外连接。 「归属专栏」MySQL 「主页链接」个人主页 「笔者」枫叶先生(fy) 目录 一、内连接二、外连接2.1 左外连接2.2 右外连接 一、内连接 内连接实际上就是利用where子句对两种表形成的笛卡儿积进行筛选&#xff0c;前面篇章学习的…

win11+vmware17+centos7.9环境搭建

温故知新 &#x1f4da;第一章 前言&#x1f4d7;背景&#x1f4d7;目标&#x1f4d7;总体方向 &#x1f4da;第二章 安装部署环境&#x1f4d7;安装VMware Workstation 17 Pro软件&#x1f4d7;安装CentOS-7虚拟机&#x1f4d5;镜像下载地址&#x1f4d5;创建虚拟机&#x1f4…