zoukankan      html  css  js  c++  java
  • 自定义校验,多个参数唯一性 springboot+mybatis-plus

    每天进步一点点,幸福自然多一点!

    前言:

    相信大家在开发项目中,添加数据或者修改数据的时候某些唯一性字段都需要进行重复验证,一般验证的情况我们思路大致如下

    以验证 code为例

    1.如果是添加操作,我们需要将编码进行查询,如果查询结果不为空,那说明有重复数据。

    select * from tablename where code = xxx

    2.如果是更新操作 ,我们不仅需要将编码进行查询,还要加个id不等于当前编辑数据的id,如果查询结果不为空,说明有重复数据

    select * from tablename where code = xxx and id != xx

    其实这样做来,我们重复编码就不出现很多,很麻烦!

    这个时候怎么办呢!

    对,你应该也想到了,自定义注解校验,通过在实体类中加入注解,来判断需要对哪些字段进行校验。

    下面是我们对字典类型为device_model中字典名称进行唯一校验

     接下来我们谈下实现:

    1.创建FileRepeat注解

     注解中有两个属性,

      ①数组类型的fields(我们需要验证的字段,因为有可能多个条件才能确定唯一,所以我们设计成数组类型)

      ②message(出现重复数据返回到前端的消息)

    import javax.validation.Constraint;
    import javax.validation.Payload;
    import java.lang.annotation.*;

    /**
    * @author xiaokang
    * @description
    * @date 2020/12/29 14:52
    */
    @Documented
    /**
    * 指定注解运用的地方:
    * ElementType.ANNOTATION_TYPE 可以给一个注解进行注解
    * ElementType.CONSTRUCTOR 可以给构造方法进行注解
    * ElementType.FIELD 可以给属性进行注解
    * ElementType.LOCAL_VARIABLE 可以给局部变量进行注解
    * ElementType.METHOD 可以给方法进行注解
    * ElementType.PACKAGE 可以给一个包进行注解
    * ElementType.PARAMETER 可以给一个方法内的参数进行注解
    * ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举
    */
    @Target({ElementType.TYPE})
    @Constraint(validatedBy = FileRepeatClass.class)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface FileRepeat {
    /**
    * 需要校验的字段
    * @return
    */
    String [] fields() default {};

    String message() default "你所输入的内容已存在";

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

    2.注解接口实现类
    import org.springframework.beans.factory.annotation.Autowired;
    import javax.validation.ConstraintValidator;
    import javax.validation.ConstraintValidatorContext;

    /**
    * @author xiaokang
    * @description
    * @date 2020/12/29 14:55
    */
    public class FileRepeatClass implements ConstraintValidator<FileRepeat,Object> {

    @Autowired
    FileRepeatUtils fileRepeatUtils;

    private String [] fileds;
    private String message;

    @Override
    public void initialize(FileRepeat validator) {
    this.fileds = validator.fields();
    this.message = validator.message();
    }

    @Override
    public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
    return fileRepeatUtils.fieldRepeat(fileds,message,o);
    }
    }

    3.这里贴上FileRepeatUtils 类,主要处理业务逻辑
    import com.baomidou.mybatisplus.activerecord.Model;
    import com.baomidou.mybatisplus.annotations.TableField;
    import com.baomidou.mybatisplus.annotations.TableId;
    import com.baomidou.mybatisplus.mapper.EntityWrapper;
    import com.orisdom.utils.BusinessException;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang3.ArrayUtils;
    import org.springframework.stereotype.Component;

    import java.lang.reflect.Field;
    import java.util.*;

    /**
    * @author xiaokang
    * @description
    * @date 2020/12/29 15:07
    */
    @Component
    @Slf4j
    public class FileRepeatUtils {

    /**
    * 实体类中id字段
    */
    private String idColumnName;

    /**
    * 实体类中id的值
    */
    private Object idColumnValue;
    /**
    *
    * @param fields 验证的字段数组
    * @param message 如果不满足返回的消息
    * @param o 实体类
    * @return
    */
    public boolean fieldRepeat(String [] fields,String message,Object o){
    try {
    // 没有校验的值返回true
    if(fields != null && fields.length == 0){
    return true;
    }
    checkUpdateOrSave(o);
    checkRepeat(fields,o,message);
    return true;
    }catch (Exception e){
    String msg = "验证字段是否重复报错";
    log.error(msg,e);
    throw new BusinessException(e.getMessage());
    }
    }

    /**
    * 通过传入的实体类中 @TableId 注解的值是否为空,来判断是更新还是保存
    * 将值id值和id列名赋值
    * id的值不为空 是更新 否则是插入
    * @param o 被注解修饰过的实体类
    * @return
    */
    public void checkUpdateOrSave(Object o) throws Exception{
    Field[] fields = o.getClass().getDeclaredFields();
    for (Field f:fields) {
    // 设置私有属性可读
    f.setAccessible(true);
    if(f.isAnnotationPresent(TableId.class)){
    TableId tableId = f.getAnnotation(TableId.class);
    idColumnName = tableId.value();
    idColumnValue = f.get(o);
    }
    }
    }

    /**
    * 通过传入的字段值获取数据是否重复
    * @param fields
    * @param o
    * @param message
    * @return
    */
    public void checkRepeat(String [] fields,Object o,String message){
    Model model = (Model) o;
    EntityWrapper entityWrapper = new EntityWrapper();
    Map<String,Object> queryMap = getColumns(fields,o);
    Iterator<Map.Entry<String, Object>> it = queryMap.entrySet().iterator();
    while (it.hasNext()) {
    Map.Entry<String, Object> entry = it.next();
    entityWrapper.eq(entry.getKey(),entry.getValue());
    }
    if(idColumnValue != null){
    //更新的话,那条件就要排除自身
    entityWrapper.ne(idColumnName,idColumnValue);
    }
    List list = model.selectList(entityWrapper);
    if(list != null && list.size()>0){
    throw new BusinessException(message);
    }
    }

    /**
    * 多条件判断唯一性,将我们的属性和值组装在map中,方便后续拼接条件
    * @param fields
    * @param o
    * @return
    */
    public Map<String,Object> getColumns(String [] fields,Object o){
    Field[] fieldList = o.getClass().getDeclaredFields();
    Map<String,Object> map = new HashMap<>();
    for (Field f : fieldList) {
    // ② 设置对象中成员 属性private为可读
    f.setAccessible(true);
    // 判断字段是否包含在数组中,如果存在,则将它对应的列字段放入map中
    if(ArrayUtils.contains(fields,f.getName())){
    getMapData(map,f,o);
    }
    }
    return map;
    }

    /**
    * 得到查询条件
    * @param map 列字段
    * @param f 字段
    * @param o 传入的对象
    */
    private void getMapData( Map<String,Object> map,Field f,Object o){
    try {
    if(f.isAnnotationPresent(TableField.class)){
    TableField tableField = f.getAnnotation(TableField.class);
    Object val = f.get(o);
    map.put(tableField.value(),val);
    }
    }catch (IllegalAccessException i){
    throw new BusinessException("获取字段的值报错");
    }
    }
    }
    4.全局异常

    import com.orisdom.dto.ApiResult;
    import com.orisdom.utils.BusinessException;
    import lombok.extern.slf4j.Slf4j;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.dao.DuplicateKeyException;
    import org.springframework.web.bind.MethodArgumentNotValidException;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.servlet.NoHandlerFoundException;

    import javax.validation.ConstraintViolationException;
    import javax.validation.ValidationException;

    /**
    * 统一异常处理
    */
    @Slf4j
    @ControllerAdvice
    public class GlobalExceptionHandler {

    private static final Logger LOG = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    @ExceptionHandler(value = BusinessException.class)
    @ResponseBody
    public ApiResult handleBusinessException(BusinessException be) {
    if (be.getStatus() == -1) {
    return ApiResult.expired(be.getMessage());
    }
    return ApiResult.fail(be.getMessage());
    }

    @ExceptionHandler(value = RuntimeException.class)
    @ResponseBody
    public ApiResult handleRuntimeException(RuntimeException e) {
    LOG.error("系统异常", e);
    return ApiResult.fail("系统异常,操作失败");
    }

    // 参数校验异常处理 ===========================================================================
    // MethodArgumentNotValidException是springBoot中进行绑定参数校验时的异常,需要在springBoot中处理,其他需要处理ConstraintViolationException异常进行处理.

    /**
    * 方法参数校验
    */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public ApiResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
    log.error("方法参数校验:{}", e.getMessage());
    return ApiResult.fail(e.getBindingResult().getFieldError().getDefaultMessage());
    }

    /**
    * ValidationException
    */
    @ExceptionHandler(ValidationException.class)
    @ResponseBody
    public ApiResult handleValidationException(ValidationException e) {
    log.error("ValidationException:", e);
    return ApiResult.fail(e.getCause().getMessage());
    }

    /**
    * ConstraintViolationException
    */
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseBody
    public ApiResult handleConstraintViolationException(ConstraintViolationException e) {
    log.error("ValidationException:" + e.getMessage(), e);
    return ApiResult.fail(e.getMessage());
    }

    @ExceptionHandler(NoHandlerFoundException.class)
    @ResponseBody
    public ApiResult handlerNoFoundException(Exception e) {
    return ApiResult.fail("路径不存在,请检查路径是否正确");
    }

    @ExceptionHandler(DuplicateKeyException.class)
    @ResponseBody
    public ApiResult handleDuplicateKeyException(DuplicateKeyException e) {
    return ApiResult.fail("数据重复,请检查后提交");
    }

    }
    demo运用示例 在实体类中加入注解,然后在controller中加入@Validated

     

     很多东西也是借鉴的,但是自己还是要动手写一遍,了解其中的原理,每天进步一点点,我们知识积累得就越多!相信有了这注解后会对我们验证重复性有了很大的代码简化!

    加油!奥利给。。

     
  • 相关阅读:
    可视化地图(从省级下钻到市级)
    全国疫情统计可视化地图
    |和||、&&和&
    MFC 常见问题
    * 和-> 优先级
    MFC控件CTabCtrl关联变量
    C++ #include—尖括号和双引号的区别
    C++类型转换
    VC++生成不同的随机数
    VS 2008 头文件库文件设置
  • 原文地址:https://www.cnblogs.com/xiaokangk/p/14208090.html
Copyright © 2011-2022 走看看