最近做项目,在使用EasyExcel做数据导入的时候,没有找到什么好的数据校验方法,很多博客都是直接写代码进行校验,感觉很麻烦,因此思考能不能有什么简便的方法。大家都知道web项目,在controller层的方法上加@Validated注解,调用接口的时候会自动进行参数校验,于是思考能不能通过同样的方法对EasyExcel导入的数据进行校验。@Validated是通过hibernate.validator包来实现的,通过看源码,发现javax.validation.ValidatorFactory,通过这个入手,写一个方便的,数据导入校验方法。
Controller层的方法,是spring框架自动调用hibernate校验方法校验,这里我们考虑手动调用hibernate校验方法,既能用hibernate本来自带的一些类似于@NotNull,@NotBlank等注解,也能通过自定义注解扩展@Constraint(validatedBy = { }),用这一套大大提高了代码的复用率。
records是EasyExcel导入定义的实体类list,headNums是表头占的行数
@Slf4j
public class ExcelValidtor {
public void valid(List records,int headNums)throws ValidException {
if(records == null || records.size() == 0){
throw new ValidException(Collections.singletonList(new ErrorMessage(ErrorCode.API,"数据不能为空")));
}
HibernateValidatorConfiguration configure = Validation.byProvider(HibernateValidator.class).configure();
ValidatorFactory validatorFactory = configure.failFast(false).buildValidatorFactory();
// 根据validatorFactory拿到一个Validator
Validator validator = validatorFactory.getValidator();
// 使用validator对结果进行校验
List errorMsgs = new ArrayList<>();
for(int a = 0;a> result = validator.validate(records.get(a));
for(ConstraintViolation constraintViolation:result){
try {
T object = (T)constraintViolation.getLeafBean();
ExcelProperty excelProperty = object.getClass().getDeclaredField(constraintViolation.getPropertyPath().toString()).getAnnotation(ExcelProperty.class);
String[] value = excelProperty.value();
String header = Arrays.stream(value).collect(Collectors.joining(","));
ErrorMessage errorMessage = new ErrorMessage(ErrorCode.API,"第"+(headNums+a+1)+"行:"+header+constraintViolation.getMessage());
errorMsgs.add(errorMessage);
} catch (NoSuchFieldException e) {
log.error(e.getMessage());
}
}
}
if (errorMsgs != null && errorMsgs.size() > 0){
throw new ValidException(errorMsgs);
}
}
}
这里的ValidException是自定义的异常类型,其实可以直接用BindException,让全局ExceptionHandler统一处理,这里由于我想加一个列名和行号,所以没这么用。列名通过反射获取@ExcelProperty的属性取得。
@Data
public class ValidException extends Exception{
private List errorMessages;
public ValidException(List errorMessages){
this.errorMessages = errorMessages;
}
public ValidException(){}
}
@Getter
@Setter
public class ErrorMessage {
private String code;
private String content;
public ErrorMessage() {
}
public ErrorMessage(String code) {
this(code, null);
}
public ErrorMessage(String code, String content) {
this.code = code;
this.content = content;
}
public static ErrorMessage of(String code, String content) {
return new ErrorMessage(code, content);
}
}
try{
ExcelValidtor excelValidtor = new ExcelValidtor();
excelValidtor.valid(uploadRecords,1);
}catch (ValidException e) {
}
{
"code": "1000",
"content": "第2行:姓名不能为null"
},
{
"code": "1000",
"content": "第3行:姓名不能为null"
},
{
"code": "1000",
"content": "第3行:年龄不能为null"
},
{
"code": "1000",
"content": "第3行:住址不能为null"
}
目前这块,我感觉行数的计算可能会有点问题,因为没有确认过,EasyExcel的导入,空行会不会给空数据,如果是直接忽略空行的话,行数就会错误。等后续有时间再确认。