实战:Excel百万级数据导入到数据库《easyPoi》

今天给大家来一个实战案例。

需求:把excel中的企业数据导入到数据库。

分析:

步骤:数据从excel中取出,封装成对象,保存到数据库

1.excel中的数据量大时,要保证执行效率

2.excel中可能会有重复数据

3.可能误操作,一个excel会导入多次,所以插入时需要检查数据库是否已存在当前要插入的数据了。

使用技术:easyPoi  官方文档:http://easypoi.mydoc.io/

开始实战:

 

    1.1maven导包easyPoi


   cn.afterturn
   easypoi-spring-boot-starter
   4.2.0


   cn.afterturn
   easypoi-base
   4.2.0


   cn.afterturn
   easypoi-web
   4.2.0


   cn.afterturn
   easypoi-annotation
   4.2.0

 

 

1.2控制器编写方法,接受一个文件对象

    @RequestMapping(value = "/importExcel", method = RequestMethod.POST)
    @ResponseBody
    public R importExcel(@RequestParam("file") MultipartFile file) {
        return enterprisesService.importExcel(file);
    }

1.3解析excel中的数据

       ImportParams params = new ImportParams();
        //标题所在行数
//        params.setTitleRows(0);
        //表头所在行数
        params.setHeadRows(1);
        Map creditcodeAndEnterprisesMap = new HashMap<>();
        //使用api获取到的List数据
        ExcelImportUtil.importExcelBySax(file.getInputStream(), EnterprisesExcelModel.class, params, new IReadHandler() {
            @Override
            public void handler(Object o) {
                EnterprisesEntity enterprisesEntity = new EnterprisesEntity();
                BeanUtils.copyProperties(o, enterprisesEntity);
                creditcodeAndEnterprisesMap.put(enterprisesEntity.getCreditcode(), enterprisesEntity);
            }

            @Override
            public void doAfterAll() {
            }
        });

 

这里使用的是 easyPoi中的    ExcelImportUtil.importExcelBySax 工具方法  主要是用于处理大量数据的   

第一个参数是 文件的input流

第二个是映射的实体类class,我的类中只有两个字段。用@Excel注解标注  name = excel中的对应的表头名称 。这里是一一对应的。

第三个是 导入的一些配置,这里 ImportParams  配置   表头所在行数 、标题所在行数。我这里无标题,表头在第一行。所以配置 params.setHeadRows(1);。也可以不配置,默认就是1

实战:Excel百万级数据导入到数据库《easyPoi》_第1张图片

第四个参数是一个读取每一行后会触发的一个触发器。我直接使用了匿名内部类。在handler方法接受一个对象。该对象就是前面配置的 EnterprisesExcelModel的类对象。

我在方法里面又创建了一个 数据库映射实体类EnterprisesEntity 的对象,并把从excel 映射 解析来的 EnterprisesExcelModel对象字段值复制给了 EnterprisesEntity 对象。

然后存到map中 key存储 creditcode企业信用代码,因为这个是唯一的不会重复的。这么做可以去除excel中的可能存在的重复数据。

1.4 去重

       List creditcodeList = new ArrayList<>();
        Set keys = creditcodeAndEnterprisesMap.keySet();
        int i = 0;
        for (String key : keys) {
            creditcodeList.add(key);
            if (i % 10000 == 0) {
                //查询和过滤重复数据
                queryAndDeleteDuplicateData(creditcodeAndEnterprisesMap, creditcodeList);
                creditcodeList.clear();
            }
            i++;
        }
        if (creditcodeList.size() > 0) {
            //查询和过滤重复数据
            queryAndDeleteDuplicateData(creditcodeAndEnterprisesMap, creditcodeList);
        }

遍历 map的key  ,用一个list去存储creditcode企业信用代码每循环一万次 就需要拿着list 去数据库查询一次,这里的1万你们可以自行设置,这里主要是做限制,如果一次性一百万条的数据去查询数据库是否存在,那就崩了。。。

  /**
     * 查询和删除重复数据
     *
     * @param creditcodeAndEnterprisesMap map
     * @param creditcodeList              要查询的 creditcode集合
     */
    private void queryAndDeleteDuplicateData(Map creditcodeAndEnterprisesMap, List creditcodeList) {
        List existEntitys = this.selectList(new EntityWrapper()
                .in("CreditCode", creditcodeList).setSqlSelect("CreditCode")
        );
        log.info("查询出已存在的数据条数:" + existEntitys.size());
        for (EnterprisesEntity existEntity : existEntitys) {
            creditcodeAndEnterprisesMap.remove(existEntity.getCreditcode());
        }
    }

查询用的in,判断数据库有没有相同的creditCode 有的话取出来,是一个集合。

然后遍历该集合,从map中删除这些数据。

1.5 批量保存

if (!creditcodeAndEnterprisesMap.isEmpty()) {
    //保存数据,设置每次批量保存的条数
    this.insertBatch(new ArrayList<>(creditcodeAndEnterprisesMap.values()), 10000);
}

注意事项:

1.要导入的excel文件大小超过20M,需要配置 application.yml

spring:
  # 设置单个文件大小
  servlet:
    multipart:
      max-file-size: 100MB
  #设置单次请求文件的总大小
      max-request-size: 100MB

2.为了提高mybatis-plush的批量保存效率,可以在配置文件中的 数据库地址后面增加参数rewriteBatchedStatements=true

原因:

MySQL Jdbc驱动在默认情况下会无视executeBatch()语句,把我们期望批量执行的一组sql语句拆散,一条一条地发给MySQL数据库,直接造成较低的性能

性能测试:

1.只解析excel数据不做保存到数据库

2.解析+保存数据库

保存到数据库大约花费了300秒,期间最耗时的是查询数据库是否有数据重复这块。这块后续还是可以继续优化的。总体 6分钟百万数据,目前的业务还是可以接受的!

各位看官大佬有什么更好的建议欢迎留言指正!

你可能感兴趣的:(实战,java,mybatis)