当前位置: 首页 > news >正文

Java 使用 Easyexcel 导出大量数据

读Excel | Easy Excel

1、 我遇到的数据量超级大,使用传统的POI方式来完成导入导出很明显会内存溢出,并且效率会非常低;2、 数据量大直接使用select * from tableName肯定不行,一下子查出来300w条数据肯定会很慢;3、 300w 数据导出到Excel时肯定不能都写在一个Sheet中,这样效率会非常低;估计打开都得几分钟;4、 300w数据导出到Excel中肯定不能一行一行的导出到Excel中。频繁IO操作绝对不行;5、 导入时300万数据存储到DB如果循环一条条插入也肯定不行;6、导入时300w数据如果使用Mybatis的批量插入肯定不行,因为Mybatis的批量插入其实就是SQL的循环;一样很慢。

准备工作

1.基于maven搭建springboot工程,引入easyexcel依赖,这里我是用的时3.0版本

   <!--EasyExcel相关依赖--><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.0.5</version></dependency>

2.创建海量数据的sql脚本

CREATE TABLE dept( /*部门表*/
deptno MEDIUMINT   UNSIGNED  NOT NULL  DEFAULT 0,
dname VARCHAR(20)  NOT NULL  DEFAULT "",
loc VARCHAR(13) NOT NULL DEFAULT ""
) ;#创建表EMP雇员
CREATE TABLE emp
(empno  MEDIUMINT UNSIGNED  NOT NULL  DEFAULT 0, /*编号*/
ename VARCHAR(20) NOT NULL DEFAULT "", /*名字*/
job VARCHAR(9) NOT NULL DEFAULT "",/*工作*/
mgr MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,/*上级编号*/
hiredate DATE NOT NULL,/*入职时间*/
sal DECIMAL(7,2)  NOT NULL,/*薪水*/
comm DECIMAL(7,2) NOT NULL,/*红利*/
deptno MEDIUMINT UNSIGNED NOT NULL DEFAULT 0 /*部门编号*/
) ;#工资级别表
CREATE TABLE salgrade
(
grade MEDIUMINT UNSIGNED NOT NULL DEFAULT 0,
losal DECIMAL(17,2)  NOT NULL,
hisal DECIMAL(17,2)  NOT NULL
);#测试数据
INSERT INTO salgrade VALUES (1,700,1200);
INSERT INTO salgrade VALUES (2,1201,1400);
INSERT INTO salgrade VALUES (3,1401,2000);
INSERT INTO salgrade VALUES (4,2001,3000);
INSERT INTO salgrade VALUES (5,3001,9999);delimiter $$#创建一个函数,名字 rand_string,可以随机返回我指定的个数字符串
create function rand_string(n INT)
returns varchar(255) #该函数会返回一个字符串
begin
#定义了一个变量 chars_str, 类型  varchar(100)
#默认给 chars_str 初始值   'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ'declare chars_str varchar(100) default'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ'; declare return_str varchar(255) default '';declare i int default 0; while i < n do# concat 函数 : 连接函数mysql函数set return_str =concat(return_str,substring(chars_str,floor(1+rand()*52),1));set i = i + 1;end while;return return_str;end $$#这里我们又自定了一个函数,返回一个随机的部门号
create function rand_num( )
returns int(5)
begin
declare i int default 0;
set i = floor(10+rand()*500);
return i;
end $$#创建一个存储过程, 可以添加雇员
create procedure insert_emp(in start int(10),in max_num int(10))
begin
declare i int default 0;
#set autocommit =0 把autocommit设置成0#autocommit = 0 含义: 不要自动提交set autocommit = 0; #默认不提交sql语句repeatset i = i + 1;#通过前面写的函数随机产生字符串和部门编号,然后加入到emp表insert into emp values ((start+i) ,rand_string(6),'SALESMAN',0001,curdate(),2000,400,rand_num());until i = max_numend repeat;#commit整体提交所有sql语句,提高效率commit;end $$#添加8000000数据
call insert_emp(100001,8000000)$$#命令结束符,再重新设置为;
delimiter ;

3.实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Emp implements Serializable {@ExcelIgnoreprivate Integer empno;@ExcelProperty(value = "员工名称")private String ename;@ExcelProperty(value = "工作")private String job;@ExcelProperty(value = "主管编号")private Integer mgr;@ExcelProperty(value = "入职日期")private Date hiredate;@ExcelProperty(value = "薪资")private BigDecimal sal;@ExcelProperty(value = "奖金")private BigDecimal comm;@ExcelProperty(value = "所属部门")private Integer deptno;}

4.vo类
@Data
public class EmpVo {@ExcelIgnoreprivate Integer empno;@ExcelProperty(value = "员工名称")private String ename;@ExcelProperty(value = "工作")private String job;@ExcelProperty(value = "主管编号")private Integer mgr;@ExcelProperty(value = "入职日期")private Date hiredate;@ExcelProperty(value = "薪资")private BigDecimal sal;@ExcelProperty(value = "奖金")private BigDecimal comm;@ExcelProperty(value = "所属部门")private Integer deptno;}

5、导出核心代码

@Resource
private EmpService empService;
/*** 分批次导出*/
@GetMapping("/export")
public void export() throws IOException {Long startTime = System.currentTimeMillis();empService.export(); //导出Long endTime = System.currentTimeMillis();Long elapsedTime = (endTime - startTime) / 1000;System.out.println("导出_方式耗时:" + elapsedTime + "s");}
public class ExcelConstants {//一个sheet装100w数据public static final Integer PER_SHEET_ROW_COUNT = 1000000;//每次查询20w数据,每次写入20w数据public static final Integer PER_WRITE_ROW_COUNT = 200000;
}

实现类中:
@Override
public void export() throws IOException {OutputStream outputStream =null;try {//记录总数:实际中需要根据查询条件进行统计即可     Integer totalCount = empMapper.selectCount(null);//每一个Sheet存放100w条数据Integer sheetDataRows = ExcelConstants.PER_SHEET_ROW_COUNT;//每次写入的数据量20w,每页查询20WInteger writeDataRows = ExcelConstants.PER_WRITE_ROW_COUNT;//计算需要的Sheet数量Integer sheetNum = totalCount % sheetDataRows == 0 ? (totalCount / sheetDataRows) : (totalCount / sheetDataRows + 1);//计算一般情况下每一个Sheet需要写入的次数(一般情况不包含最后一个sheet,因为最后一个sheet不确定会写入多少条数据)Integer oneSheetWriteCount = sheetDataRows % writeDataRows == 0 ? (sheetDataRows / writeDataRows) : (sheetDataRows / writeDataRows + 1);//计算最后一个sheet需要写入的次数Integer lastCountAll = totalCount - (sheetNum-1)*sheetDataRows;Integer lastSheetWriteCount = lastCountAll % writeDataRows == 0 ? (lastCountAll / writeDataRows) : (lastCountAll / writeDataRows + 1);ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletResponse response = requestAttributes.getResponse();outputStream = response.getOutputStream();//必须放到循环外,否则会刷新流ExcelWriter excelWriter = EasyExcel.write(outputStream).build();//开始分批查询分次写入for (int i = 0; i < sheetNum; i++) {//创建SheetWriteSheet sheet = new WriteSheet();sheet.setSheetName("测试Sheet1"+i);sheet.setSheetNo(i);//循环写入次数: j的自增条件是当不是最后一个Sheet的时候写入次数为正常的每个Sheet写入的次数,如果是最后一个就需要使用计算的次数lastSheetWriteCountfor (int j = 0; j < (i != sheetNum - 1 ? oneSheetWriteCount : lastSheetWriteCount); j++) {//分页查询一次20wPage<Emp> page = empMapper.selectPage(new Page(j + 1 + oneSheetWriteCount * i, writeDataRows), null);List<Emp> empList = page.getRecords();//使用pagehelper的如下
// int  pageNum = j + 1 + oneSheetWriteCount * i;                    //PageHelper.startPage(pageNum,writeDataRows,getOrderBy(pageable.getSort())).setReasonable(true);
//List<DqtbsHiddenDangerQuery> list = mapper.expmortByPage(dto);//封装成可以导出实体类List<EmpVo> empVoList = new ArrayList<>();for (Emp emp : empList) {EmpVo empVo = new EmpVo();BeanUtils.copyProperties(emp, empVo);empVoList.add(empVo);}WriteSheet writeSheet = EasyExcel.writerSheet(i, "员工信息" + (i + 1)).head(EmpVo.class).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).build();//写数据excelWriter.write(empVoList, writeSheet);}}// 下载EXCELresponse.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");response.setCharacterEncoding("utf-8");// 这里URLEncoder.encode可以防止浏览器端导出excel文件名中文乱码 当然和easyexcel没有关系String fileName = URLEncoder.encode("员工信息", "UTF-8").replaceAll("\\+", "%20");response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");excelWriter.finish();outputStream.flush();} catch (IOException e) {e.printStackTrace();} catch (BeansException e) {e.printStackTrace();}finally {if (outputStream != null) {outputStream.close();}}
}

前端vue的:

  //导出exportExcel(values) {let this_ = thisthis_.spinningExport = truethis.$api.export(BASE_URL + 'exportNew', values, `数据查询_${moment(new Date()).format('YYYY-MM-DD')}.xlsx`, {success() {this_.spinningExport = false},fail() {this_.spinningExport = false}})},

分批量查询,例如,一个sheet 页 存储 10000条,分页每次查1000条,每个sheet需要查10次。总数据50000条,就分了5个sheet页来显示,大批量数据来说,easyExcel还是很好用的。

http://www.lryc.cn/news/190061.html

相关文章:

  • 高效防汛决策:山海鲸可视化系统助力城市防洪
  • 易点云CFO向征:CFO不能只讲故事,价值创造才是核心
  • 【计算机网络】poll | epoll
  • C++设计模式_07_Bridge 桥模式
  • [JAVA版本] Websocket获取B站直播弹幕——基于直播开放平台
  • 第一个 Python 程序
  • 广告牌安全监测,保障户外广告牌的安全与稳定
  • 分类预测 | MATLAB实现KOA-CNN-GRU开普勒算法优化卷积门控循环单元数据分类预测
  • 进来了解实现官网搜索引擎的三种方法
  • OpenCV3-Python(7)模板匹配和霍夫检测
  • [C++11]花括号{}、initializer_list、auto、decltype
  • 在Android平板上使用code-server公网远程Ubuntu服务器编程
  • 宝塔上安装mysql版本比较和区别
  • uniapp微信小程序自定义封装分段器。
  • 文心一言 VS 讯飞星火 VS chatgpt (111)-- 算法导论10.2 2题
  • 小程序中使用echarts的相关配置以及折线图案例(简单易懂)
  • 前端面试回答不好的问题总结
  • 漏洞预警|CVE-2023-38545 Curl 和 libcurl 堆缓冲区溢出漏洞
  • 【Java 进阶篇】HTML 语义化标签详解
  • 【思维构造】Element Extermination—CF1375C
  • CSP模拟53联测15 D. 子序列
  • iceberg-flink 十一:在dlink代码中建表增加catalog地址。
  • 多列等高实现
  • 2023 泰山杯 --- Crypto wp
  • 蓝桥杯每日一题20233.10.10
  • 366. 寻找⼆叉树的叶⼦节点
  • python - excel 设置样式
  • Gemmini测试test文件chisel源码详解(一)
  • RabbitMQ中的手动应答和自动应答
  • 【C语言】文件的操作与文件函数的使用(详细讲解)