为项目添加 HibernateValidator

news2024/11/24 13:26:52


HibernateValidatorhttps://hibernate.org/validator/

引入依赖项

首先,确保已将Hibernate Validator添加到Maven或Gradle依赖项中:

<!-- Maven 依赖 -->
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.2.0.Final</version>
</dependency>

在Spring配置中,添加以下代码以启用Hibernate Validator:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;

@Configuration
public class Config {
    
    @Bean
    public Validator validator(AutowireCapableBeanFactory autowireCapableBeanFactory) {
        ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
                .configure()
// autowireCapableBeanFactory 是为了方便自定义验证器时候使用Spring Bean
                .constraintValidatorFactory(new SpringConstraintValidatorFactory(autowireCapableBeanFactory))
                // 快速失败模式
//                .failFast(true)
                .buildValidatorFactory();
        return validatorFactory.getValidator();
    }
}

添加异常解析器

import lombok.Data;
import com.alibaba.fastjson.JSON;
import org.springframework.web.bind.annotation.*;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;

@ControllerAdvice
public class ControllerAdvice {
     
    /**
     *  RequestParam 注解的校验异常
     * @param e
     * @return
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public Response processRequestParameterException(MissingServletRequestParameterException e) {
        return Response.illegalParameter(e.getMessage(), e.getMessage());
    }

    /**
     * Validated 注解的校验异常
     */
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Response<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        return wrapErrors(e.getBindingResult().getAllErrors());
    }

    /**
     * 提取 validate 异常信息
     *
     * @param errors
     * @return
     */
    private Response<?> wrapErrors(List<ObjectError> errors) {
        if (CollectionUtils.isEmpty(errors)) {
            return SimpleResponse.illegalParameter("请求参数错误");
        }
        List<String> errorMsgList = errors.stream().map(
                        objectError -> objectError.getDefaultMessage())
                .collect(Collectors.toList());
        String msg = JSON.toJSONString(errorMsgList).replaceAll("\"", "");
        return SimpleResponse.illegalParameter(msg, msg);
    }
}

@Data
class Response{
    private String respCode;
    private String msg;
    private Data data;

    public SimpleResponse(){}

    /**
     * 数据校验失败
     *
     * @param msg 校验失败的原因
     * @param <T> 泛型
     * @return 泛型
     */
    public static <T> Response<T> illegalParameter(String msg) {
        SimpleResponse<T> response = new SimpleResponse<>();
        response.setRespCode(VERIFY_FAIL.getCode());
        response.setMsg(msg);
        return response;
    }

    /**
     * 数据校验失败
     *
     * @param msg 校验失败的原因
     * @param <T> 泛型
     * @return 泛型
     */
    public static <T> Response<T> illegalParameter(String msg,T data) {
        SimpleResponse<T> response = new SimpleResponse<>();
        response.setRespCode(VERIFY_FAIL.getCode());
        response.setMsg(msg);
        response.setData(data);
        return response;
    }
}

使用方法

在Controller 层的使用

注解方式的使用  @Validated 支持分组验证 @RequestParam 自带校验

import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.stereotype.Controller;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;


@RestController
@RequestMapping("/export")
public class ExportController {

    @Resource
    LogService logService;

    @PostMapping(name = "添加", value = "/insert.json")
    public Response<?> insert(@RequestBody @Validated({LogService.insert.class})
                                            LogDTO dto) {
        logService.insert(dto);
        return Response.ofSuccess();
    }

    @GetMapping(name = "重新生成", value = "/reGenerate.json")
    public Response<?> reGenerate(@RequestParam(value = "id") Long id) {
        return logService.reGenerate(id);
    }

    @GetMapping(name = "删除", value = "/logicDelete.json")
    public Response<?> logicDelete(@RequestParam(value = "id") Long id) {
        return logService.logicDelete(id, getUsername());
    }

    @PostMapping(name = "导出文件", value = "/downloadFile.json")
    public void downloadFile(HttpServletResponse response, @RequestBody @Validated({LogService.downloadFile.class}) LogDTO dto) {
        omcCloseOrderExportLogService.downloadFile(response, dto);
    }
}

Bean注解的应用

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

import javax.validation.constraints.NotNull;
import javax.validation.constraints.Null;
import java.util.Date;

@Data
@AllArgsConstructor
public class LogDTO {
    /**
     * 主键
     */
    @Null(message = "无需id主键", groups = {LogService.insert.class})
    @NotNull(message = "主键id不得为空", groups = {LogService.reGenerate.class,
            LogService.logicDelete.class,
            LogService.downloadFile.class})
    private Long id;

    private String traceId;

    /**
     * 用户身份证号
     */
    @NotNull(message = "用户身份证号不得为空", groups = {OmcCloseOrderExportLogService.insert.class})
    private String idCard;

    /**
     * 操作人
     */
    @NotNull(message = "操作人不得为空", groups = {LogService.insert.class})
    private String operator;


    /**
     * 下载类型
     */
    @NotNull(message = "下载类型不得为空", groups = {LogService.downloadFile.class})
    private String downloadType;

    public LogDTO() {
    }

}

接口定义

public interface LogService {
    /**
     *  验证组-添加
     */
    @interface insert{}

    /**
     *  验证组-重新生成
     */
    @interface reGenerate{}

    /**
     * 验证组-逻辑删除
     */
    @interface logicDelete{}

    /**
     *  验证组-下载文件
     */
    @interface downloadFile{}

    /**
     * 添加记录
     */
    void insert(LogDTO logDTO);

    /**
     * 更新 记录的状态、数据
     */
    Response<?> reGenerate(Long id);

    /**
     * 删除数据
     */
    Response<?> logicDelete(Long id, String operator);

    /**
     *  下载文件
     */
    void downloadFile(HttpServletResponse response, LogDTO logDTO);

   

在非Controller层的使用

本质上是代理

import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.AssertTrue;

@Service
@Validated
public class OServiceImpl implements OService {

    @Resource
    OMapper oMapper;

    /**
     * 获取当前类的代理对象
     *
     */
    private OServiceImpl getProxy() {
        return (OServiceImpl) AopContext.currentProxy();
    }
    
    // 存在自己调用自己的情况(isExistName、isExistFullName 都被增强了)所以需要通过代理上下文调用
    @Override
    public void validSave(OrganizationDTO organizationDTO) {
        OServiceImpl oService = getProxy();

        oService.isExistName(organizationDTO.getOrganizationName());
        oService.isExistFullName(organizationDTO.getFullName());
        oService.isNotExistUserPhone(organizationDTO.getPhone());
    }
    
     @AssertTrue(message = "重复的名")
    public Boolean isExistName(String name) {
        return organizationMapper.nameIsExist(name) == null;
    }

    @AssertTrue(message = "重复的全称")
    public Boolean isExistFullName(String fullName) {
        return oMapper.fullNameIsExist(fullName) == null;
    }

    @AssertTrue(message = "重复的手机号")
    public Boolean isNotExistPhone(String phone) {
        return oMapper.phoneIsExist(phone) == null;
    }


}

class OMapper{
    
    /**
     * 检查名字是否已经存在
     */
    @Select("SELECT 1 FROM db WHERE f_name = #{name} LIMIT 1")
    Integer nameIsExist(String name);

    /**
     * 检查手机号是否已经存在
     */
    @Select("SELECT 1 FROM db WHERE f_phone = #{phone} LIMIT 1")
    Integer phoneIsExist(@Param("phone") String phone);
}
public Response<?> save(ODTO odto){
    oService.validSave(odto);
    oService.save(odto);
    return Response.ofSuccess();
}

实体中带有isXX方式的自动验证

@Controller 添加验证注解时候,ODTO 的isBeforeDate 会被自动执行

import lombok.Data;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotEmpty;

import javax.validation.constraints.Future;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Null;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.AssertTrue;
import java.io.Serializable;
import java.util.Date;

@Data
public class ODTO {

    /**
     * 业务ID
     */
    @NotNull(groups = OService.updateById.class, message = " 业务ID不得为空")
    private String oNo;

   

    /**
     * 全称
     */
    @NotEmpty(message = "全称不得为空")
    @Length(min = 3, max = 100, message = "全称字符长度应为{min}-{max}")
    private String fullName;

    /**
     * 编码
     */
    @Null(message = "不需要编码")
    private String code;

    /**
     *  负责人
     */
    @NotEmpty
    @Length(min = 2, max = 50, message = "联系人,字符长度应为{min}-{max}")
    private String person;

    /**
     * 手机号
     */
    @NotEmpty
    @Pattern(regexp = "(13\\d|14[579]|15[^4\\D]|17[^49\\D]|18\\d|19\\d)\\d{8}",
            message = "请检查手机号格式")
    private String phone;

    /**
     * 模式, 自定义注解CheckEnum 用以检查固定的值枚举值
     */
    @NotNull
    @CheckEnum(DicEnum.chargingMode)
    private Integer chargingMode;


    /**
     * 开始时间
     */
    @NotNull
    private Date startDate;

    /**
     * 结束时间
     */
    @NotNull
    @Future(message = "结束时间应大于当前日期")
    private Date endDate;


    public ODTO() {
    }

    /**
     *  标注了validation的非Get/Set 方法应以is开头
     * @return
     */
    @AssertTrue(message = "开始时间 应小于 结束时间")
    public boolean isBeforeDate(){
        return this.startDate.before(this.endDate);
    }
}

自定义验证器

/**
 * 定义注解
 */
@Documented
@Retention(RUNTIME)
// 定义 接口约束验证器
@Constraint(validatedBy = {CheckEnumValidatorForString.class, CheckEnumValidatorForNumber.class})
@Target({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE, TYPE_USE})
public @interface CheckEnum {

    /**
     * 多个值分割的形式
     */
    String MULTIPLE_VALUE_SEPARATOR = ",";

    String message() default "枚举检查失败";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};

    DicEnum value();


}


// 定义枚举
public enum DicEnum {
    /**
     * 类型
     */
    oType
}

import javax.annotation.Resource;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Arrays;
import java.util.List;


@Component
public class CheckEnumValidatorForString implements ConstraintValidator<CheckEnum, String> {
    /**
     * 多个值分割的形式
     */
    String MULTIPLE_VALUE_SEPARATOR = ",";

    private List<String> codeListCache;

    CheckEnum constraintAnnotation;

    @Resource
    DictionaryCache dictionaryCache;

    @Override
    public void initialize(CheckEnum constraintAnnotation) {
        // 先执行的方法
        this.constraintAnnotation = constraintAnnotation;
        this.codeListCache = dictionaryCache.logicExpirationDictCodeListCache(constraintAnnotation.value());
    }

    /**
     * true 通过校验
     *
     * @param value
     * @param context
     * @return
     */
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (value == null) {
            return true;
        }
        this.codeListCache = dictionaryCache.logicExpirationDictCodeListCache(constraintAnnotation.value());
        List<String> cachedDictCodeList = this.codeListCache;
        if (cachedDictCodeList.size() == 0) {
            return false;
        }
        if (value.contains(MULTIPLE_VALUE_SEPARATOR)) {
            String[] str = value.split(MULTIPLE_VALUE_SEPARATOR);
            return cachedDictCodeList.containsAll(Arrays.asList(str));
        }
        return cachedDictCodeList.contains(value);
    }
}

更多实现

休眠验证程序 8.0.0.Final - Jakarta Bean 验证参考实现:参考指南 (jboss.org)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/616885.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

三年功能测试经验,投了几百份简历,为什么没有收到offer?

软件测试行业3年多经验&#xff0c;学历大专自考本科&#xff0c;主要测试方向web&#xff0c;PC端&#xff0c;wap站&#xff0c;小程序公众号都测试过&#xff0c;app也测过一些&#xff0c;C端B端都有&#xff0c;除功能外&#xff0c;接口性能也有涉猎&#xff0c;但是不能…

kafka--多易杰哥讲解

Kafka是一种分布式的流式数据平台&#xff0c;广泛应用于实时流数据处理和消息系统。它可以让处理数据的应用程序能够处理高流量的数据流&#xff0c;同时提供可靠性和可扩展性。 【多易教育】-Kafka文档 1.基本概念 1.1什么是kafka Kafka 最初是由 LinkedIn 即领英公司…

教你如何用fiddler抓取https(详细教程)

对于想抓取HTTPS的测试初学者来说&#xff0c;常用的工具就是fiddler&#xff0c;可是在初学时&#xff0c;大家对于fiddler如何抓取HTTPS真是伤了脑筋&#xff0c;可能你一步步按着网上的帖子成功了&#xff0c;那当然是极好的&#xff0c;有可能没有成功&#xff0c;这时候你…

前端基础(JavaScript)——基础语法(变量,分支...) Json对象【重要】 函数定义 事件(未完待续)

目录 引出JS是啥&#xff0c;能干啥基础语法1.变量----let2.怎么打印---console3.if条件分支--啥都可以是条件例子&#xff1a;输入框&#xff0c;打印输入和未输入4.数组push 和 splice&#xff08;2&#xff09;splice&#xff0c;3个参数&#xff0c;索引开始、删除几个&…

广域网技术

广域网互连一般采用在网络层进行协议转换的方法实现。时延网关&#xff0c;更确切的说是路由器。 无连接的网际互连&#xff1a; 在网际层提供路由信息的是路由表&#xff0c;每个站或者路由器中都有一个网际路由表&#xff0c;表的每一行说明一个目标站对应的路由器地址。 路…

如何在Ubuntu20.04中配置 libarchive 库

libarchive 是什么&#xff1f; libarchive是一个开源的压缩和归档库。 它支持实时访问多种压缩文件格式&#xff0c;比如7z、zip、cpio、pax、rar、cab、uuencode等&#xff0c;因此应用十分广泛。 举个例子&#xff0c;我写了一段代码&#xff0c;想要解压一个压缩包&#…

HARVEST基音检测算法

Harvest: A high-performance fundamental frequency estimator from speech signals一种基于语音信号的高性能基频估计算法 Harvest的独特之处在于可以获得可靠的F0轮廓&#xff0c;减少了将浊音部分错误地识别为清音部分的错误。 它包括两个步骤:估计F0候选点和在这些候选点…

17JS08——函数

函数 一、函数的概念二、函数的使用2.1 声明函数2.2 调用函数2.3 函数的封装 三、函数的参数3.1 形参和实参3.2 形参和实参个数不匹配问题3.3 小结 四、函数的返回值4.1 return语句4.2 return终止函数4.3 break、continue、return的区别4.4 案例 五、arguments的使用案例1&…

案例30:基于Springboot酒店管理系统开题报告设计

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

Java-多线程解析1

一、线程的描述&#xff1a; 1、线程是一个应用程序进程中不同的执行路径比例如&#xff1a;一个WEB服务器&#xff0c;能够为多个用户同时提供请求服务&#xff1b;而 -> 进程是操作系统中正在执行的不同的应用程序,比如&#xff1a;我们可以同时打开系统的word和游戏 2、多…

Tomcat优化及Nginx、tomcat动静分离配置

Tomcat优化及Nginx、tomcat动静分离配置 一、Tomcat优化1、操作系统优化&#xff08;内核参数优化&#xff09;2、Tomacat配置文件优化3、Java虚拟机&#xff08;JVM&#xff09;调优 二、Nginx、tomcat动静分离配置(七层代理)三、四层代理 一、Tomcat优化 Tomcat默认安装下的…

八、进程等待

文章目录 一、进程创建&#xff08;一&#xff09;fork函数概念1.概念2.父子进程共享fork之前和fork之后的所有代码&#xff0c;只不过子进程只能执行fork之后的&#xff01; &#xff08;二&#xff09;fork之后&#xff0c;操作系统做了什么?1.进程具有独立性&#xff0c;代…

(二)CSharp-字段-属性-常量

一、字段 什么是字段 字段&#xff08;filed&#xff09;是一种表示与对象或类型&#xff08;类或结构体&#xff09;关联的变量字段是类型的成员&#xff0c;旧称“成员变量”与对象关联的字段亦称“实例字段”与类型关联的字段称为“静态字段”&#xff0c;由 static 修饰 …

java SSM 学生家长联系系统myeclipse开发mysql数据库springMVC模式java编程计算机网页设计

一、源码特点 java SSM学生家长联系系统是一套完善的web设计系统&#xff08;系统采用SSM框架进行设计开发&#xff0c;springspringMVCmybatis&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用…

docker 安装 oracle19c

docker 安装 oracle19c 拉取镜像 sudo docker pull registry.cn-hangzhou.aliyuncs.com/zhuyijun/oracle:19c创建挂载目录 sudo mkdir -p /mydata/oracle/oradata授权 sudo chmod 777 /mydata/oracle/oradata安装 sudo docker run -d \ -p 1521:1521 -p 5500:5500 \ -e ORACLE…

《C++高级编程》读书笔记(三:编码风格)

1、参考引用 C高级编程&#xff08;第4版&#xff0c;C17标准&#xff09;马克葛瑞格尔 2、建议先看《21天学通C》 这本书入门&#xff0c;笔记链接如下 21天学通C读书笔记&#xff08;文章链接汇总&#xff09; 1. 为代码编写文档 在编程环境下&#xff0c;文档通常指源文件中…

Android系统的Ashmem匿名共享内存子系统分析(5)- 实现共享的原理

声明 其实对于Android系统的Ashmem匿名共享内存系统早就有分析的想法&#xff0c;记得2019年6、7月份Mr.Deng离职期间约定一起对其进行研究的&#xff0c;但因为我个人问题没能实施这个计划&#xff0c;留下些许遗憾…文中参考了很多书籍及博客内容&#xff0c;可能涉及的比较…

springboot源码分析-jar启动

概述 Spring Boot 提供了 Maven 插件 spring-boot-maven-plugin&#xff0c;可以方便的将 Spring Boot 项目打成 jar 包或者 war 包。 SpringBoot 是如何通过jar包启动的 java -jar做了什么&#xff1f;看看官网怎么说 If the -jar option is specified, its argument is the …

基础算法(一)——补

快排 归并排序和快速排序的时间复杂度都是 O(nlogn) 左右两边设置哨兵&#xff0c;分成左边小于x, 右边大于x。 &#xff08;先分交换&#xff0c; 再递归&#xff09; #include<iostream> using namespace std; const int N1e610; int n; int q[N]; void quick_sort(i…

【计算机视觉】手把手教你配置stable-diffusion-webui进行AI绘图(保姆级教程)

文章目录 一、前言二、本地化部署的要求三、使用的项目Stable diffusion WebUI项目四、电脑环境配置4.1 安装Anaconda4.2 看版本4.3 配置库包下载环境&#xff0c;加快网络速度4.4 创建环境4.5 激活环境4.6 安装git4.7 安装cuda 五、Stable diffusion环境配置5.1 下载Stable di…