首页 > 编程语言 >Java 自定义注解校验字段唯一性

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

时间:2022-12-27 11:33:35浏览次数:47  
标签:Java String 自定义 fields 校验 param return message class

业务场景

在项目中,某些情景下我们需要验证编码是否重复,账号是否重复,身份证号是否重复等...

那么有没有办法可以解决这类似的重复代码量呢?

我们可以通过自定义注解校验的方式去实现,如下: 在实体类上面加上自定义的注解即可
@FieldRepeatValidator(field = "account", message = "账号已存在!")

实现步骤

1.引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
    <version>2.3.11.RELEASE</version>
</dependency>

2.新增自定义注解

// 注意:实体类必须继承Model,且需要标明表名@TableName("t_test"),校验字段需要加上注解 @TableField("name")
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = FieldRepeatValidator.class)
public @interface FieldRepeat {

    /**
     * 需要检验的字段
     * @return
     */
    String[] field() default {};

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

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

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

3.新增@FieldRepeat注解接口实现类

public class FieldRepeatValidator implements ConstraintValidator<FieldRepeat, Object> {

    @Autowired
    private FieldRepeatUtils fieldRepeatUtils;

    private String[] fields;
    private String message;

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

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

4.新增字段内容重复判断处理工具类

@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("获取字段的值错误");
        }
    }
}

测试类

@Data
@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;
}

标签:Java,String,自定义,fields,校验,param,return,message,class
From: https://www.cnblogs.com/zhaojinhui/p/17007697.html

相关文章