Hibernate Validator详解

内容不多耐心看完 建议手动操作一下即可 (文章 需要会swagger 也可以进行相应的忽略,springboot 整合 swagger链接:https://blog.csdn.net/weixin_40516924/article/details/103443607

一、简介

项目中,难免需要对参数 进行一些参数正确性的效验,这些小样出现在业务代码中,多次出现if效验数据使得业务代码显得臃肿,所以Hibernate validator框架刚好解决这些问题,可以很优雅的方式实现参数的效验,让业务代码和小样逻辑分开,不在编写重复的效验逻辑hibernate Validator提供了JSR303规范中所有内置约束的实现,除此之外还有一些附加约束。

Bean Validator为JavaBean验证定义了相关数据模型和API。缺省的元数据是Java Annotations,通过XML可以对原有的元数据信息进行覆盖和扩展,Bean validation是一个运行时数据验证框架,在验证之后的验证的错误信息会被马上返回。

二、作用

  • 验证逻辑与业务之间进行了分离,降低了程序耦合度;
  • 统一且规范的验证方式,无需你再次编写重复的验证代码;
  • 开发人员更加专注于你的业务,将这些繁琐的事情统统丢一边;

三、使用方法

开发中,主要用于 接口api的入参效验 封装工具类 在代码中效验两种使用方法。

注意:在springboot中 不需要引入Hibernate Validator , 因为 在引入的 spring-boot-starter-web(springbootweb启动器)依赖的时候中,内部已经依赖了 hibernate-validator 依赖包

 

1、常用的注解

  • @NotEmpty   用在集合类  ————验证注解的元素不为null 不为空
  • @NotBlank    用在String上面  ————验证注解的元素 不为null 不为空
  • @NotNull      用在基本类型上(不包含基本数据类型)  ————验证注解的元素值不是null
  • @Valid          启用效验
注解适用的数据类型说明
@AssertFalseBoolean, boolean验证注解的元素值是false
@AssertTrueBoolean, boolean验证注解的元素值是true
@DecimalMax(value=x)BigDecimalBigIntegerStringbyteshortintlong和原始类型的相应包装。HV额外支持:NumberCharSequence的任何子类型。验证注解的元素值小于等于@ DecimalMax指定的value值
@DecimalMin(value=x)BigDecimalBigIntegerStringbyteshortintlong和原始类型的相应包装。HV额外支持:NumberCharSequence的任何子类型。验证注解的元素值小于等于@ DecimalMin指定的value值
@Digits(integer=整数位数, fraction=小数位数)BigDecimalBigIntegerStringbyteshortintlong和原始类型的相应包装。HV额外支持:NumberCharSequence的任何子类型。验证注解的元素值的整数位数和小数位数上限
@Futurejava.util.Datejava.util.Calendar; 如果类路径上有Joda Time日期/时间API ,则由HV附加支持:ReadablePartialReadableInstant的任何实现。
 
验证注解的元素值(日期类型)比当前时间晚
@Max(value=x)BigDecimalBigIntegerbyteshortintlong和原始类型的相应包装。HV额外支持:CharSequence的任何子类型(评估字符序列表示的数字值),Number的任何子类型。验证注解的元素值小于等于@Max指定的value值
@Min(value=x)BigDecimalBigIntegerbyteshortintlong和原始类型的相应包装。HV额外支持:CharSequence的任何子类型(评估char序列表示的数值),Number的任何子类型。验证注解的元素值大于等于@Min指定的value值
@NotNull 
任意种类
验证注解的元素值不是null
@Null 
任意种类
验证注解的元素值是null
@Pastjava.util.Date,java.util.Calendar; 如果类路径上有Joda Time日期/时间API ,则由HV附加支持:ReadablePartial和ReadableInstant的任何实现。
 
验证注解的元素值(日期类型)比当前时间早
@Pattern(regex=正则表达式, flag=)串。HV额外支持:CharSequence的任何子类型。验证注解的元素值与指定的正则表达式匹配
@Size(min=最小值, max=最大值)字符串,集合,映射和数组。HV额外支持:CharSequence的任何子类型。验证注解的元素值的在min和max(包含)指定区间之内,如字符长度、集合大小
@Valid 
Any non-primitive type(引用类型)
验证关联的对象,如账户对象里有一个订单对象,指定验证订单对象
@NotEmptyCharSequence,Collection, Map and Arrays验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0)
@Range(min=最小值, max=最大值)CharSequence, Collection, Map and Arrays,BigDecimal, BigInteger, CharSequence, byte, short, int, long 以及原始类型各自的包装验证注解的元素值在最小值和最大值之间
@NotBlankCharSequence验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的空格
@Length(min=下限, max=上限)CharSequence验证注解的元素值长度在min和max区间内
@EmailCharSequence验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式

 

2、案列

 

在对象参数上添加验证注解

@Data
public class TestReqVO {

    @ApiModelProperty(value = "名称")
    @NotBlank(message = "name 不能为空")
    private String name;

    @ApiModelProperty(value = "年龄")
    @NotNull(message = "age 不能为空")
    private Integer age;

    @ApiModelProperty(value = "id集合")
    @NotEmpty(message = "id 集合不能为空")
    private List<String> ids;
}

 

 

 controller中 @Valid注解 指定 需要效验参数 

@Api(tags = "测试模块")
@RestController
@RequestMapping("/test")
public class TestController {
    
    @PostMapping("/valid/error")
    @ApiOperation(value = "测试Validator抛出业务异常接口")
    public DataResult testvalidError(@RequestBody @Valid TestReqVO vo){
        return DataResult.ok();
    }

}

 

捕获controller中的全局异常


/**
 * @Author 18011618
 * @Description
 * @Date 16:38 2018/7/5
 * @Modify By
 */
@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {

    private static final String logExceptionFormat = "Capture Exception By GlobalExceptionHandler: Code: %s Detail: %s";
    private static Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);


    //数据效验异常 (针对处理validation 框架)
    @ExceptionHandler(value = {BindException.class, MethodArgumentNotValidException.class})
    public Object validationExceptionHandler(Exception ex) {
        return validationDataResultFormat(14, ex);
    }

    //记录日志
    private <T extends Throwable> void log(Integer status, T exception) {
        exception.printStackTrace();
        log.error(String.format(logExceptionFormat, status, exception.getMessage()));
    }


    private <T extends Throwable> DataResult validationDataResultFormat(Integer status, T exception) {
        log(status, exception);  // 记录日志
        BindingResult bindResult = null;
        if (exception instanceof BindException) {
            bindResult = ((BindException) exception).getBindingResult();
        } else if (exception instanceof MethodArgumentNotValidException) {
            bindResult = ((MethodArgumentNotValidException) exception).getBindingResult();
        }
        String msg;
        if (bindResult != null && bindResult.hasErrors()) {
            msg = bindResult.getAllErrors().get(0).getDefaultMessage();
            if (msg.contains("NumberFormatException")) {
                msg = "参数类型错误!";
            }
        } else {
            msg = "系统繁忙,请稍后重试...";
        }
        return DataResult.build(status, msg);
    }


}

 

 

DataResult是自定义响应结构(使用自己项目的相应结构):


import java.util.List;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * 自定义响应结构
 */
public class DataResult {

    // 定义jackson对象
    private static final ObjectMapper MAPPER = new ObjectMapper();

    // 响应业务状态
    private Integer status;

    // 响应消息
    private String msg;

    // 响应中的数据
    private Object data;

    public static DataResult build(Integer status, String msg, Object data) {
        return new DataResult(status, msg, data);
    }

    public static DataResult ok(Object data) {
        return new DataResult(data);
    }

    public static DataResult ok() {
        return new DataResult(null);
    }

    public DataResult() {

    }

    public static DataResult build(Integer status, String msg) {
        return new DataResult(status, msg, null);
    }

    public DataResult(Integer status, String msg, Object data) {
        this.status = status;
        this.msg = msg;
        this.data = data;
    }

    public DataResult(Object data) {
        this.status = 200;
        this.msg = "OK";
        this.data = data;
    }

//    public Boolean isOK() {
//        return this.status == 200;
//    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    /**
     * 将json结果集转化为DataResult对象
     * 
     * @param jsonData json数据
     * @param clazz DataResult中的object类型
     * @return
     */
    public static DataResult formatToPojo(String jsonData, Class<?> clazz) {
        try {
            if (clazz == null) {
                return MAPPER.readValue(jsonData, DataResult.class);
            }
            JsonNode jsonNode = MAPPER.readTree(jsonData);
            JsonNode data = jsonNode.get("data");
            Object obj = null;
            if (clazz != null) {
                if (data.isObject()) {
                    obj = MAPPER.readValue(data.traverse(), clazz);
                } else if (data.isTextual()) {
                    obj = MAPPER.readValue(data.asText(), clazz);
                }
            }
            return build(jsonNode.get("status").intValue(), jsonNode.get("msg").asText(), obj);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * 没有object对象的转化
     * 
     * @param json
     * @return
     */
    public static DataResult format(String json) {
        try {
            return MAPPER.readValue(json, DataResult.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Object是集合转化
     * 
     * @param jsonData json数据
     * @param clazz 集合中的类型
     * @return
     */
    public static DataResult formatToList(String jsonData, Class<?> clazz) {
        try {
            JsonNode jsonNode = MAPPER.readTree(jsonData);
            JsonNode data = jsonNode.get("data");
            Object obj = null;
            if (data.isArray() && data.size() > 0) {
                obj = MAPPER.readValue(data.traverse(),
                        MAPPER.getTypeFactory().constructCollectionType(List.class, clazz));
            }
            return build(jsonNode.get("status").intValue(), jsonNode.get("msg").asText(), obj);
        } catch (Exception e) {
            return null;
        }
    }

}

 

 

 

 

3、测试

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Ark方舟

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

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

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

打赏作者

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

抵扣说明:

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

余额充值