优化EasyExcel导入
EasyExcel 导入可以参考我这篇文章 http://nwjshm.cn/archives/48.html 在真实场景的情况下Excel数据量很庞大,如果需要做一些字典转换,io一次数据库的时间挺大的
优化思路
- 使用
Mybatis-Plus
sql拦截器实现真批量添加 - 如果需要io数据库 我建议可以把字典表的数据缓存到
redis
或者存储为map
集合数据 减少io数据库次数
实现
- 重构EasyExcel监听器
@Slf4j
public abstract class ExcelListener<T,R> extends AnalysisEventListener<T> {
/**
* 自定义用于暂时存储data
* 可以通过实例获取该值
*/
private List<T> datas = new ArrayList<>(2000);
/**
* 每解析一行都会回调invoke()方法
* @param data 读取后的数据对象
* @param context 内容
*/
@Override
public abstract void invoke(T data, AnalysisContext context);
/**
* 读取完后操作
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
//判断集合是否为空
if (oConvertUtils.listIsEmpty(datas)) {
log.info("Excel中没有数据");
throw new ApiException(503, "Excel中没有数据,请检查模版是否正确");
}
log.info("所有数据读取完成");
}
/**
* 异常方法 (类型转换异常也会执行此方法) (读取一行抛出异常也会执行此方法)
*
* @param exception
* @param context
* @throws Exception
*/
@Override
public void onException(Exception exception, AnalysisContext context) {
log.info("有异常");
// 如果是某一个单元格的转换异常 能获取到具体行号
// 如果要获取头的信息 配合invokeHeadMap使用
if (exception instanceof ExcelDataConvertException) {
ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;
log.error("第{}行,第{}列解析异常,数据为:{}", (excelDataConvertException.getRowIndex() 1),
(excelDataConvertException.getColumnIndex() 1), excelDataConvertException.getCellData());
throw new ApiException("第" (excelDataConvertException.getRowIndex() 1) "行,第" (excelDataConvertException.getColumnIndex() 1) "列格式转换错误");
}
throw new ApiException(exception.getMessage());
}
/**
* 返回数据
* @return 返回读取的数据集合
**/
public List<T> getDatas() {
return datas;
}
}
将监听器的每一行读取方法定义成抽象方法。
- 实现
Mybatis-plus
真新增
/**
* @author :扫地僧
* @date :2023/09/8 0008 10:59
* @version: V1.0
* @slogan: 天下风云出我辈,一入代码岁月催
* @description: 批量插入 SQL 注入器
**/
public class InsertBatchSqlInjector extends DefaultSqlInjector {
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
List<AbstractMethod> methodList = super.getMethodList(mapperClass);
//更新时自动填充的字段,不用插入值
methodList.add(new InsertBatchSomeColumn(i -> i.getFieldFill() != FieldFill.UPDATE));
return methodList;
}
}
代码语言:javascript复制@Configuration
public class MybatisPlusConfig {
@Bean
public InsertBatchSqlInjector sqlInjector() {
return new InsertBatchSqlInjector();
}
}
代码语言:javascript复制public interface CommonMapper<T> extends BaseMapper<T> {
/**
* 全量插入,等价于insert
* @param entityList
* @return
*/
int insertBatchSomeColumn(List<T> entityList);
}
将所有的mapper类本来继承BaseMapper的全都替换成 CommonMapper
- 定义分批List集合的工具类
/**
* @author :扫地僧
* @date :2023/09/13 0013 15:21
* @version: V1.0
* @slogan: 天下风云出我辈,一入代码岁月催
* @description: 通用分批次list集合工具
**/
public class ListBatcherUtils<T> {
/**
*
* @param inputList 集合数据
* @param batchSize 分批大小
* @return
*/
public List<List<T>> batchList(List<T> inputList, int batchSize) {
List<List<T>> batchedLists = new ArrayList<>();
for (int i = 0; i < inputList.size(); i = batchSize) {
int end = Math.min(i batchSize, inputList.size());
List<T> batch = inputList.subList(i, end);
batchedLists.add(batch);
}
return batchedLists;
}
}
分批的原因是:拦截器实现的是因为sql语句有大小限制,如果超了就会报错
- 具体导入代码
@RestController
@RequestMapping("test/systest")
public class SysTestController {
@Autowired
private SysTestService sysTestService;
/**
* 模版导入
* @param file
* @return
* @throws IOException
*/
@PostMapping("upload")
@ResponseBody
public Result upload(MultipartFile file) throws IOException {
return sysTestService.upload(file);
}
}
代码语言:javascript复制 @Override
public Result upload(MultipartFile file) throws IOException {
ExcelListener<SysTestEntityy> excelListener = new ExcelListener<SysTestEntity>() {
private List<SysTestEntity> datas = getDatas();
@Override
public void invoke(SysTestEntityy data, AnalysisContext context) {
//具体业务处理
datas.add(data);
}
};
//headRowNumber(1)从第2行开始读取,使用getDatas()方法取出数据
EasyExcel.read(file.getInputStream(), OdsFpZzsXiaoxiangOrgEntity.class, excelListener)
.headRowNumber(1)
.sheet(0)
.doRead();
List<SysTestEntityy> list = excelListener.getDatas();
//如果List集合数据小于1500不分批直接导入
if (list.size() < 1500) {
baseMapper.insertBatchSomeColumn(list);
return ResultUtil.success("", "导入成功" list.size() "条");
}
//如果List集合数据大于1500分批导入
ListBatcherUtils<SysTestEntityy> listBatcherUtils = new ListBatcherUtils<>();
List<List<SysTestEntityy>> lists = listBatcherUtils.batchList(list, 1500);
for (List<SysTestEntityy> sysTestEntityy : lists) {
baseMapper.insertBatchSomeColumn(sysTestEntityy);
}
return ResultUtil.success("", "导入成功" list.size() "条");
}