spring boot 自定义注解校验字段唯一性

通过在实体类中加入注解,来判断需要对哪些字段进行校验。

FieldRepeat接口
import org.springframework.messaging.handler.annotation.Payload;

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

@Documented
/**
 * 指定注解运用的地方:
 * ElementType.ANNOTATION_TYPE 可以给一个注解进行注解
 * ElementType.CONSTRUCTOR 可以给构造方法进行注解
 * ElementType.FIELD 可以给属性进行注解
 * ElementType.LOCAL_VARIABLE 可以给局部变量进行注解
 * ElementType.METHOD 可以给方法进行注解
 * ElementType.PACKAGE 可以给一个包进行注解
 * ElementType.PARAMETER 可以给一个方法内的参数进行注解
 * ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举
 **/
// 警告:实体类必须继承Model,且需要标明表名,校验字段需加上 @TableField
@Target({ElementType.TYPE})
@Constraint(validatedBy = FieldRepeatClass.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface FieldRepeat {
    /**
     * 需要校验的字段
     * @return
     */
    String [] fields() default {};

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

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

    Class<? extends Payload>[]  payload() default {};
}
FieldRepeatClass.class
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

/**
 * 注解接口实现
 */
public class FieldRepeatClass implements ConstraintValidator<FieldRepeat, Object> {

    @Autowired
    FieldRepeatUtils fieldRepeatUtils;

    private String [] fileds;
    private String message;

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

    @SneakyThrows
    @Override
    public boolean isValid(Object o, ConstraintValidatorContext constraintValidatorContext) {
        return fieldRepeatUtils.fieldRepeat(fileds,message,o);
    }
}
FieldRepeatUtils.class
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.stereotype.Component;

import javax.xml.bind.ValidationException;
import java.lang.reflect.Field;
import java.util.*;


@Component
@Slf4j
public class FieldRepeatUtils {

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

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

    /**
     * 通过传入的实体类中 @TableId 注解的值是否为空,来判断是更新还是保存
     * 将值id值和id列名赋值
     * id的值不为空 是更新 否则是插入
     * @param o 被注解修饰过的实体类
     * @return
     */
    public void checkUpdateOrSave(Object o) throws IllegalAccessException{
        Field[] fields = getAllFields(o.getClass());
        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 clazz 当前类对象
     * @return 字段数组
     */
    private static Field[] getAllFields(Class<?> clazz) {
        List<Field> fieldList = new ArrayList<>();
        while (clazz != null){
            fieldList.addAll(new ArrayList<>(Arrays.asList(clazz.getDeclaredFields())));
            clazz = clazz.getSuperclass();
        }
        Field[] fields = new Field[fieldList.size()];
        return fieldList.toArray(fields);
    }


    /**
     * 通过传入的字段值获取数据是否重复
     * @param fields
     * @param o
     * @param message
     * @return
     */
    public void checkRepeat(String [] fields,Object o,String message) throws ValidationException, IllegalAccessException {
        Model model = (Model) o;
        //Mybatis-plus 3.0以下用EntityWrapper
        QueryWrapper<Object> qw = new QueryWrapper<>(); 
        Map<String,Object> queryMap = getColumns(fields,o);
        for (Map.Entry<String, Object> entry : queryMap.entrySet()) {
            qw.eq(entry.getKey(), entry.getValue());
        }
        if(idColumnValue != null){
            //更新的话,那条件就要排除自身
            qw.ne(idColumnName,idColumnValue);
        }
        List list = model.selectList(qw);
        if(list != null && list.size()>0){
            throw new ValidationException(message);
        }
    }


        /**
         * 多条件判断唯一性,将我们的属性和值组装在map中,方便后续拼接条件
         * @param fields
         * @param o
         * @return
         */
    public Map<String,Object> getColumns(String [] fields,Object o) throws IllegalAccessException {
        Field[] fieldList = getAllFields(o.getClass());
        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) throws IllegalAccessException {
        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 IllegalAccessException("获取字段的值错误");
        }
    }
}

测试类

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import com.saimo.huagong.business.base.bean.Base;
import com.saimo.huagong.config.FieldRepeat.FieldRepeat;
import lombok.Data;
import lombok.EqualsAndHashCode;

import javax.validation.constraints.NotNull;


@Data
@EqualsAndHashCode(callSuper = false)
@TableName("t_test")
@FieldRepeat(fields = {"name"},message = "名称不能重复,请重新输入",groups = {Base.Save.class,Base.Update.class})
public class Test extends Model<Test>{

    @TableId(value = "id", type = IdType.AUTO)
    @NotNull(message = "id不能为空",groups = {Base.Update.class,Base.Delete.class,Base.Detail.class})
    private Long id;

    @TableField("name")
    @NotNull(message = "名称不能为空",groups = {Base.Update.class,Base.Save.class})
    private String name;

}

注:需要有一个Mapper接口,即使里面什么也没有

public interface TestMapper extends BaseMapper<Test> {
}

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值