• 基于EasyExcel的大数据量导入并去重


    源码:https://gitee.com/antia11/excel-data-import-demo
    背景:客户需要每周会将上传一个 Excel 数据文件,数据量单次为 20W 以上,作为其他模块和报表的基础数据。

    客户需求分析:#

    1. 数据量为 20W 条左右。
    2. 数据需要去重。
    3. 等待时间不能太长。
    4. 文件中会有错误数据存在,错误数据跳过不进入数据库。

    注意点:#

    1. 为提高导入速度,选择分批插入,每次插入 1000 条数据。
    2. 在读取数据时判断数据是否正确,不正确不插入。
    3. 对数据进行去重。

    实现逻辑:#

    1. 首先使用 EasyExcel 实现分批插入数据。
    2. 数据插入完成后,在数据库使用 SQL 的方式进行去重,避免内存溢出。
    package com.antia1.demo.service;
    
    import com.alibaba.excel.EasyExcel;
    import com.antia1.demo.entity.ExcelDataEntity;
    import com.antia1.demo.listener.ExcelDataListener;
    import com.antia1.demo.mapper.ExcelDataMapper;
    import com.antia1.demo.util.RespBean;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.web.multipart.MultipartFile;
    
    import java.io.IOException;
    import java.util.Map;
    
    /**
     * Author: anti
     * Date: 2022/7/23 16:13
     */
    @Service
    @Slf4j
    public class ExcelDataService {
    
        @Autowired
        private ExcelDataMapper excelDataMapper;
    
    
        public RespBean importData(MultipartFile file) throws IOException {
            //0.获取数据库中的最大id
            Map idMap = excelDataMapper.getMaxId();
            int maxId = Integer.parseInt(idMap.get("maxId") + "");
    
            //1.读取excel
            EasyExcel.read(file.getInputStream(), ExcelDataEntity.class,new ExcelDataListener(excelDataMapper,maxId)).sheet().doRead();
    
            //2.开始去除重复数据
            log.debug("全部导入完成,开始进行数据去重");
            int count = excelDataMapper.deleteDuplicates();
            log.debug("去除重复数据:{}条",count);
    
            return RespBean.ok("导入完成");
        }
    }
    
    
    折叠
    package com.antia1.demo.listener;
    
    import com.alibaba.excel.context.AnalysisContext;
    import com.alibaba.excel.event.AnalysisEventListener;
    import com.antia1.demo.entity.ExcelDataEntity;
    import com.antia1.demo.mapper.ExcelDataMapper;
    import lombok.extern.slf4j.Slf4j;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * Author: anti
     * Date: 2022/7/23 16:10
     */
    @Slf4j
    public class ExcelDataListener extends AnalysisEventListener {
    
        private static final int BATCH_COUNT = 1000;
    
        private List list = new ArrayList<>();
    
        private ExcelDataMapper excelDataMapper;
    
        private int primaryKey;
    
        private int totalCount;
    
        public ExcelDataListener(ExcelDataMapper excelDataMapper, int primaryKey) {
            this.excelDataMapper = excelDataMapper;
            this.primaryKey = primaryKey;
        }
    
        @Override
        public void invoke(ExcelDataEntity excelDataEntity, AnalysisContext analysisContext) {
            primaryKey ++ ;
            excelDataEntity.setId(String.valueOf(primaryKey));
            list.add(excelDataEntity);
            if(list.size() >= BATCH_COUNT){
                saveData();
                list.clear();
            }
        }
    
        @Override
        public void doAfterAllAnalysed(AnalysisContext analysisContext) {
            saveData();
            System.out.println(String.format("数据同步完成,总数量为:%s",totalCount));
        }
    
    
        public void saveData(){
            if(list.size()>0){
                int count = excelDataMapper.insertBatch(list);
                totalCount += count;
            }
        }
    }
    
    
    折叠
    
    mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    
    <mapper namespace="com.antia1.demo.mapper.ExcelDataMapper">
      
      
      <insert id="insertBatch" parameterType="java.util.List">
        INSERT INTO `demo`.`tb_exceldata` (
        `id`,
        `code`,
        `desc`,
        `objectCode`,
        `projectCode`,
        `other`
        )
        VALUES
        <foreach collection="list" item="item" separator=",">
          (#{item.id}, #{item.code}, #{item.desc}, #{item.objectCode},#{item.projectCode},#{item.other})
        foreach>
      insert>
      
      
      <select id="getMaxId" resultType="java.util.Map">
        SELECT IFNULL(MAX(CAST(id AS SIGNED)),0) AS maxId FROM `demo`.`tb_exceldata`
      select>
      
      
      <delete id="deleteDuplicates">
        DELETE
        FROM
        `tb_exceldata`
        WHERE
        id NOT IN (
        SELECT
        t.id
        FROM
        ( SELECT MIN( id ) AS id FROM `tb_exceldata` GROUP BY `code`,`desc`,`objectCode`,`projectCode`,`other`) t
        )
      delete>
      
    mapper>
    
    
    折叠
  • 相关阅读:
    如何实现Cloneable接口?深拷贝和浅拷贝的区别?
    LED主流光源-环形光源
    带你了解基于Ploto构建自动驾驶平台
    Linux文件权限
    javascript中的错误类型
    嵌入式分享合集51
    ChatGPT 即将诞生一周年,OpenAI 将有大动作
    aosp集成证书和过root检测
    智能家居离线语音识别控制系统设计(SU-03T)
    数据库常用的数据类型和约束条件
  • 原文地址:https://www.cnblogs.com/antaia11/p/16515420.html