项目在做导入时,每个模块的导入都要写数据校验,这样抽出共通比较方便且代码复用率高
@PostMapping("/import")
public AjaxResult importAnalysis(MultipartFile file) {
try {
//异步操作
// BaseDataListener dataListener = new BaseDataListener<IBbbApplication>(iBbbApplication, "importBbb", 2000);
// EasyExcel.read(file.getInputStream(),
// SpecialEquipmentTemp.class,
// dataListener
// ).sheet().doRead();
//EasyExcel支持对实体类的属性进行注解,方便后续进行读写操作
List<BbbTemp> objects = EasyExcel.read(file.getInputStream()).head(BbbTemp.class).sheet().doReadSync();
//处理校验
List<String> emsg = ExcelUtil.doValidate(objects);
if (emsg.size() > 0) {
return new AjaxResult().message("1", JSON.toJSONString(emsg));
} else {
//处理业务数据
List<String> result = iBbbApplication.import(objects);
if (result.size() > 0) {
return new AjaxResult().message("1", JSON.toJSONString(result));
}else {
return new AjaxResult().success("保存成功");
}
}
} catch (Exception e) {
return error("导入失败");
} finally {
}
}
共同类
public class ExcelUtil {
public static String validate(@Valid Object data, int rowNum) {
Set<ConstraintViolation<Object>> validate = Validation.buildDefaultValidatorFactory()
.getValidator()
.validate(data, new Class[0]);
if (CollectionUtils.isNotEmpty(validate)) {
StringBuilder msg = new StringBuilder();
msg.append("第" + (rowNum + 2) + "行: ");
for (ConstraintViolation<Object> cv : validate) {
msg.append(cv.getMessage()).append(",");
}
msg.delete(msg.length() - 1, msg.length());
return msg.toString();
}
return null;
}
public static List<String> doValidate(List datas) {
StringBuilder msg = new StringBuilder();
List<Object> list = new ArrayList<>();
List<String> errorMsgs = new ArrayList<>();
datas.stream().forEach(item -> {
int i = datas.indexOf(item);
String strings = validate(item, i);
if (strings != null) {
errorMsgs.add(strings);
}
if (!list.contains(item)) {
list.add(item);
} else {
msg.append("第" + (i + 3) + "行: 数据与之前数据重复");
errorMsgs.add(msg.toString());
}
});
return errorMsgs;
}
}
异步的话需要个监听类 防止数据量大导致OOM
public class BaseDataListener<T> extends AnalysisEventListener<T> {
private static final Logger LOGGER = LoggerFactory.getLogger(BaseDataListener.class);
/**
* 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
*/
private int batchCount = 100;
List<T> list = new ArrayList<T>();
private Object dataService;
private String methodName;
/**
* 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
*/
public BaseDataListener() {
// 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
}
/**
* 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
*/
public BaseDataListener(Object object, String methodName, int size) {
this.dataService = object;
this.methodName = methodName;
this.batchCount = size;
}
public BaseDataListener(Object object, String methodName) {
this(object, methodName, 100);
}
/**
* 这个每一条数据解析都会来调用
*
* @param data one row value. Is is same as {@link AnalysisContext#readRowHolder()}
* @param context
*/
@Override
public void invoke(T data, AnalysisContext context) {
LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));
String msg = ExcelUtil.validate(data, context.readRowHolder().getRowIndex());
if (msg != null) {
list.add(data);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (list.size() >= batchCount) {
saveData();
// 存储完成清理 list
list.clear();
}
}
}
/**
* 所有数据解析完成了 都会来调用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
LOGGER.info("所有数据解析完成!");
saveData();
}
/**
* 加上存储数据库
*/
private void saveData() {
LOGGER.info("{}条数据,开始存储数据库!", list.size());
Class<?> clz = dataService.getClass();
try {
Method method = clz.getMethod(methodName, List.class);
method.invoke(dataService, list);
} catch (NoSuchMethodException e) {
LOGGER.error("未在服务类内找到{}方法", methodName);
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
LOGGER.info("存储数据库成功!");
}
}