需求描述

需要对列表数据中某个字段验重。

解决方案

通过自定义注解,借助 spring 参数校验框架,抽象通用逻辑,通过反射获取字段值进行比较。

注解 - NoRepeat

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* 非重复校验
*
* @author daiwenzh5
* @date 2021/11/22
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Repeatable(NotRepeat.List.class)
@Constraint(validatedBy = NotRepeatConstraintValidator.class)
public @interface NotRepeat {

String field();

String message() default "{javax.validation.constraints.NotRepeat.message}";

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

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

@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
@Documented
@interface List {
NotRepeat[] value();
}

}

校验逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/**
* 非重复字段校验
*
* @author daiwenzh5
* @date 2021/11/22
*/
public class NotRepeatConstraintValidator implements ConstraintValidator<NotRepeat, List<?>> {

private String field;

private String message;

@Override
public void initialize(NotRepeat constraintAnnotation) {
field = constraintAnnotation.field();
message = constraintAnnotation.message();
}

@Override
public boolean isValid(List<?> value, ConstraintValidatorContext context) {
val first = value.stream()
// 获取字段值
.map(this::getValue)
// 去除 null 值
.filter(Objects::nonNull)
// 按值分组,并统计数量
.collect(Collectors.groupingBy(
Function.identity(),
Collectors.counting()
)).entrySet()
.stream()
// 筛选数量大于 1
.filter(it -> it.getValue() > 1)
// 取第一个数
.findFirst()
.map(Map.Entry::getKey);
if (first.isPresent()) {
// 禁用默认模板信息
context.disableDefaultConstraintViolation();
// 此处使用 String.format 组装,支持模板字符串
context.buildConstraintViolationWithTemplate(String.format(
message,
first.get()
))
.addConstraintViolation();
return false;
}
return true;
}

private Object getValue(Object object) {
// 此处采用的 hutool 的反射工具,可以酌情替换
return ReflectUtil.getFieldValue(object, field);
}
}

评论