前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >优化EasyExcel导入

优化EasyExcel导入

作者头像
@依然范特西
发布2023-10-18 14:17:23
3620
发布2023-10-18 14:17:23
举报
文章被收录于专栏:云天的博客

优化EasyExcel导入

EasyExcel 导入可以参考我这篇文章 http://nwjshm.cn/archives/48.html 在真实场景的情况下Excel数据量很庞大,如果需要做一些字典转换,io一次数据库的时间挺大的

优化思路

  1. 使用Mybatis-Plus sql拦截器实现真批量添加
  2. 如果需要io数据库 我建议可以把字典表的数据缓存到redis 或者存储为map 集合数据 减少io数据库次数

实现

  1. 重构EasyExcel监听器
代码语言:javascript
复制
@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;
    }

 
}

将监听器的每一行读取方法定义成抽象方法。

  1. 实现Mybatis-plus 真新增
代码语言:javascript
复制
/**
 * @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

  1. 定义分批List集合的工具类
代码语言:javascript
复制
/**
 * @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语句有大小限制,如果超了就会报错

  1. 具体导入代码
代码语言:javascript
复制
@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() + "条");

    }
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-09-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 优化EasyExcel导入
    • 优化思路
      • 实现
      相关产品与服务
      数据库
      云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档