源码: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<String, Object> 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<ExcelDataEntity> { private static final int BATCH_COUNT = 1000; private List<ExcelDataEntity> 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;
}
}
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE 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> <!--查询最大id-->
<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>

基于EasyExcel的大数据量导入并去重的更多相关文章

  1. Mysql 大数据量导入程序

    Mysql 大数据量导入程序<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" ...

  2. java excel大数据量导入导出与优化

    package com.hundsun.ta.utils; import java.io.File; import java.io.FileOutputStream; import java.io.I ...

  3. JAVA JDBC大数据量导入Mysql

    转自https://blog.csdn.net/q6834850/article/details/73726707?tdsourcetag=s_pctim_aiomsg 采用JDBC批处理(开启事务. ...

  4. 【Java POI】POI基于事件驱动解析大数据量2007版本Excel,空值导致列错位问题

    1.目前测试了20M的文件,可以读取. 2.支持单个工作表1万+的数据行数,耗时如图. 3.以下是关键地方处理的代码 //Accepts objects needed while parsing. / ...

  5. SQL Server 使用bcp进行大数据量导出导入

    转载:http://www.cnblogs.com/gaizai/archive/2010/04/17/1714389.html SQL Server的导出导入方式有: 在SQL Server中提供了 ...

  6. 使用OPENROWSET、Microsoft.ACE.OLEDB实现大数据量的高效导入

    首先说明使用的环境是:java和Sqlserver. 最近公司需要进行大数据量的导入操作.原来使用的是Apache POI,虽然可以实现功能,但是因为逻辑处理中需要进行许多校验,处理速度太慢,使用多线 ...

  7. MYSQL数据库导入大数据量sql文件失败的解决方案

    1.在讨论这个问题之前首先介绍一下什么是"大数据量sql文件". 导出sql文件.选择数据库-----右击选择"转储SQL文件"-----选择"结构和 ...

  8. MySQL数据库如何解决大数据量存储问题

    利用MySQL数据库如何解决大数据量存储问题? 各位高手您们好,我最近接手公司里一个比较棘手的问题,关于如何利用MySQL存储大数据量的问题,主要是数据库中的两张历史数据表,一张模拟量历史数据和一张开 ...

  9. 利用MySQL数据库如何解决大数据量存储问题?

    提问:如何设计或优化千万级别的大表?此外无其他信息,个人觉得这个话题有点范,就只好简单说下该如何做,对于一个存储设计,必须考虑业务特点,收集的信息如下:1.数据的容量:1-3年内会大概多少条数据,每条 ...

随机推荐

  1. KD-Tree及希尔伯特空间填充曲线的应用

    引言 我们可能会有这样的一种需求,像是打车软件中呼叫附近的车来接送自己,或者是在qq中查看附近的人.我们都需要知道距离自己一定范围内的其它目标的集合.如果将上面举例的功能抽象出来,就是要实现以某个点为 ...

  2. python操作MySQL,SQL注入的问题,SQL语句补充,视图触发器存储过程,事务,流程控制,函数

    python操作MySQL 使用过程: 引用API模块 获取与数据库的连接 执行sql语句与存储过程 关闭数据库连接 由于能操作MySQL的模块是第三方模块,我们需要pip安装. pip3 insta ...

  3. netty系列之:netty中常用的xml编码解码器

    目录 简介 XmlFrameDecoder XmlDecoder 总结 简介 在json之前,xml是最常用的数据传输格式,虽然xml的冗余数据有点多,但是xml的结构简单清晰,至今仍然运用在程序中的 ...

  4. Vue组件之间通信

    vue组件传值有以下几种情况: 父组件向子组件传值.子组件向父组件传值.兄弟组件之间传值等 一.父组件向子组件传值: 传值方式: props <father> // 动态传递值 <s ...

  5. MYSQL如何比对版本号字符串

    MYSQL如何比对版本号字符串 mysql 数据库中存储了一个形如"2.7.6.526" 的版本号,现在要获取出小于某个版本号的数据,怎么做?这个就是昨天遇到的一个问题,记录下查到 ...

  6. 基于.NetCore开发博客项目 StarBlog - (6) 页面开发之博客文章列表

    系列文章 基于.NetCore开发博客项目 StarBlog - (1) 为什么需要自己写一个博客? 基于.NetCore开发博客项目 StarBlog - (2) 环境准备和创建项目 基于.NetC ...

  7. 监控工具:nmon

    软件介绍 分析工具 分析 AIX 和 Linux 性能的免费工具,   这个高效的工具可以工作于任何哑屏幕.telnet 会话.甚至拨号线路.另外,它并不会消耗大量的 CPU 周期,通常低于百分之二. ...

  8. 好客租房18-jsx阶段总结

    JSX 1jsx是react的核心内容 2jsx是在js代码中写HTML结构,是react中声明式的提现 3使用jsx配合嵌入的js表达式,条件渲染,列表渲染,可以描述任意ui结构 4推荐使用cals ...

  9. C# 蓄水池抽样

    蓄水池采样算法解决的是在给定但长度未知的大数据集中,随机等概率抽取一个数据.如果知道数据的长度,可以用随机数rand()%n得到一个确切的随机位置,或者分块取值来构造随机,那么该位置的对象就是所求的对 ...

  10. vue虚拟dom和diff算法

    vue的虚拟dom和diff算法 1.虚拟dom 虚拟dom,我的理解就是通过js对象的方式来具体化每一个节点,把dom树上面的每个节点都变为对象里的一个元素,元素的子元素变为子节点,节点上面的cla ...