您现在的位置是:首页 >其他 >为项目添加 HibernateValidator网站首页其他

为项目添加 HibernateValidator

W|J 2024-08-26 12:01:03
简介为项目添加 HibernateValidator


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)

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。