Hibernate Validator 讲解(声明Bean与各级别约束)
field级别约束
bean校验中的约束是通过Java注解的形式表现出来的(例如,@NotNull是一个非空约束),再细分的话,有4种类型的约束,它们分别是:field级别的约束、property级别的约束、容器元素的约束和类级别的约束。
field级别的约束的例子
package com.lyj.demo.pojo;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import com.lyj.demo.utils.JacksonUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.builder.ToStringBuilder;
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
/**
* @author 凌兮
* @date 2020/9/2 13:44
* hibernate Validator测试类
*/
@Data
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
@AllArgsConstructor
@NoArgsConstructor
public class ValidatorT {
@NotNull
private Integer returnOrderId;
@AssertTrue
private Boolean isRegistered;
@NotBlank
private String goodsName;
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
// return JacksonUtil.toJson(this);
}
}
小结:
当使用field级别的约束的时候,hibernate validator会使用field访问策略来访问需要校验的值,这意味着,校验引擎直接访问实例变量而不是通过调用属性访问方法(getter方法),即使属性访问方法存在也不行。`
`任何访问级别的field都可以使用约束(public private等),但是static的不行。
Property级别约束
本文讲解property级别的约束。如果我们的模型类使用的是标准的Java bean的话,我们也可以使用property级别的约束来替代field级别的约束。
Property级别约束例子
package com.lyj.demo.pojo;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.builder.ToStringBuilder;
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* @author 凌兮
* @date 2020/9/2 13:44
* hibernate Validator property级别的约束测试类
*/
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
public class ValidatorT2 {
private Integer returnOrderId;
private Boolean isRegistered;
private String goodsName;
@NotNull
public Integer getReturnOrderId() {
return returnOrderId;
}
public void setReturnOrderId(Integer returnOrderId) {
this.returnOrderId = returnOrderId;
}
@AssertTrue
public Boolean getRegistered() {
return isRegistered;
}
public void setRegistered(Boolean registered) {
isRegistered = registered;
}
@NotBlank
public String getGoodsName() {
return goodsName;
}
public void setGoodsName(String goodsName) {
this.goodsName = goodsName;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
// return JacksonUtil.toJson(this);
}
}
小结
当使用property级别的约束的时候,hibernate validator会使用property访问策略来访问需要校验的值,校验引擎通过getter方法访问要被校验的值。`
`建议在一个模型类中,field级别的约束和property级别的约束不要同时使用,否则会导致一个属性被校验2次。
不建议使用Property级别约束,不方便也不太美观
类级别约束
field级别的约束和property级别的约束都是针对单个属性的,而类级别的属性则是针对整个对象的,这在一个对象的多个属性之间具有相关性的情况下是非常有用的。
类级别约束例子
自定义注解
/**
* @author 凌兮
* @date 2020/9/2 18:54
* 类级别约束测试注解
*/
@Target({
ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = {
PassengerValidor.class})
public @interface ValidPassengerAnnotation {
String message() default "人员超载";
Class<?>[] groups() default {
};
Class<? extends Payload> [] payload() default {
};
}
自定义注解校验规则
/**
* @author 凌兮
* @date 2020/9/2 19:01
* 类级别约束校验拦截器校验
*/
public class PassengerValidor implements ConstraintValidator<ValidPassengerAnnotation, ValidatorT3> {
@Override
public void initialize(ValidPassengerAnnotation constraintAnnotation) {
}
@Override
public boolean isValid(ValidatorT3 validatorT3, ConstraintValidatorContext constraintValidatorContext) {
if (Objects.isNull(validatorT3)) {
return true;
}
return validatorT3.getSeatNum() <= validatorT3.getStudentList().size();
}
}
pojo
/**
* @author 凌兮
* @date 2020/9/2 13:44
* hibernate Validator 类级别约束测试类
*/
@Data
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
@AllArgsConstructor
@NoArgsConstructor
@ValidPassengerAnnotation
public class ValidatorT3 {
private Integer returnOrderId;
private Boolean isRegistered;
private String goodsName;
private Integer seatNum;
private List<Student> studentList;
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
// return JacksonUtil.toJson(this);
}
}
小结
通过上面案例,我们只要了解到如果一个对象的多个属性之间有约束关系,那么我们可以使用class级别的来校验就行了,有关自定义注解和拦截器使用可以自行百度。
继承之Property级别约束
继承之Property级别约束例子
当一个类实现了某接口或者继承了某类的时候,父类型的约束将会被子类型继承。
父类:
package com.lyj.demo.pojo;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.builder.ToStringBuilder;
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* @author 凌兮
* @date 2020/9/2 13:44
* hibernate Validator 继承之Property级别约束测试类
*/
@Data
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
@AllArgsConstructor
@NoArgsConstructor
public class ValidatorT4 {
private Integer returnOrderId;
private Boolean isRegistered;
private String goodsName;
@NotBlank(message = "制造商不能为空")
private String manufacture;
private Integer seatNum;
/**
* 对于父类继承子类并且属性相同,只能覆盖父类的属性,父类的属性会被隐藏,但父类的方法
* 不会被隐藏,只会被覆盖,所以对于父类和子类的属性相同的注解校验,
* 子类要覆盖父类的属性方法,并且不能使用@Data自动生成的,否则失效
*
* @return
*/
@Min(value = 2, message = "人数不能小于2")
public Integer getSeatNum() {
return seatNum;
}
public void setSeatNum(Integer seatNum) {
this.seatNum = seatNum;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
// return JacksonUtil.toJson(this);
}
}
子类:
package com.lyj.demo.pojo;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.builder.ToStringBuilder;
import javax.validation.constraints.AssertTrue;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* @author 凌兮
* @date 2020/9/2 13:44
* hibernate Validator 继承之Property级别约束测试类
*/
@Data
@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class)
@AllArgsConstructor
@NoArgsConstructor
//@EqualsAndHashCode(callSuper = true) //继承父类
public class ValidatorT5 extends ValidatorT4 {
private Integer orderGoodsId;
private Boolean isCreated;
@NotBlank(message = "租赁站不能为空")
private String relationStation;
private Integer seatNum;
/**
* 对于父类继承子类并且属性相同,只能覆盖父类的属性,父类的属性会被隐藏,但父类的方法
* 不会被隐藏,只会被覆盖,所以对于父类和子类的属性相同的注解校验,
*子类要覆盖父类的属性方法,并且不能使用@Data自动生成的,否则失效
* 并且父类和子类相同属性的注解校验可以共同起作用。
* 尽管这里添加最小值校验,但和父类的最小值校验一起生效,当小于0时,使用子类报错,当大于0小于2时使用父类的报错
* @return
*/
@Max(value = 5, message = "人数不能大于5")
@Min(value = 0, message = "人数不能小于0")
@Override
public Integer getSeatNum() {
return seatNum;
}
@Override
public void setSeatNum(Integer seatNum) {
this.seatNum = seatNum;
}
@Override
public String toString() {
return ToStringBuilder.reflectionToString(this);
// return JacksonUtil.toJson(this);
}
}
测试方法;
@RequestMapping(value = "/HOME/ValidTest4", method = RequestMethod.POST)
@ApiOperation(value = "Validator 继承级别约束测试", notes = "Hibernate Validator测试")
public BaseResponse<Void> Validator4Test() {
ValidatorT5 validatorT5 = new ValidatorT5();
// 设置违反了来自父类的约束
validatorT5.setManufacture(null);
validatorT5.setRelationStation(null);
// 父类和子类的约束可以累加
validatorT5.setSeatNum(1);
// 获取Validator工厂接口方法
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
Validator validator = validatorFactory.getValidator();
Set<ConstraintViolation<ValidatorT5>> validate = validator.validate(validatorT5);
logger.info("