使用EasyExcel和多线程实现高效数据导出

server/2025/2/23 0:09:07/

EasyExcel_1">使用EasyExcel和多线程实现高效数据导出

1. 概述

在企业级应用中,数据导出是一个常见的需求。为了提高导出效率,尤其是在处理大量数据时,我们可以结合使用EasyExcel库和多线程技术。本文将详细介绍如何通过EasyExcel和多线程技术实现高效的数据导出功能。

2. 环境准备

2.1 Java版本

  • Java版本:本项目基于Java开发。

2.2 依赖库

  • com.alibaba:easyexcel: 用于Excel文件的读写操作。
  • org.springframework:spring-jdbc: 提供JDBC模板类,简化数据库操作。

3. 代码结构与逻辑

3.1 类定义

java">import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.*.*.business.core.domain.ExportParam;
import lombok.AllArgsConstructor;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

3.2 主要方法解析

3.2.1 exportData

该方法负责设置HTTP响应头,并调用EasyExcel进行Excel文件的生成。同时,它利用多线程分页查询数据,以提升性能。

java">  public void exportData(ExportParam<?> exportParam, HttpServletResponse response) throws IOException {// 设置响应头response.setContentType("application/vnd.ms-excel");response.setCharacterEncoding("utf-8");String fileName = URLEncoder.encode(exportParam.getFileName(), "UTF-8");response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".xlsx");// 执行导出try (ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream(), exportParam.getEntityClass()).build()) {WriteSheet writeSheet = EasyExcel.writerSheet("Sheet1").build();int total = getTotalCount(exportParam.getSql());int totalPages = (total + exportParam.getPageSize() - 1) / exportParam.getPageSize();ExecutorService executor = Executors.newFixedThreadPool(exportParam.getThreadCount());List<Future<List<?>>> futures = new ArrayList<>();for (int page = 1; page <= totalPages; page++) {int startRow = (page - 1) * exportParam.getPageSize();int endRow = page * exportParam.getPageSize();futures.add(executor.submit(new QueryTask(jdbcTemplate,exportParam.getSql(),exportParam.getEntityClass(),startRow,endRow)));}for (int i = 0; i < totalPages; i++) {List<?> data = futures.get(i).get();excelWriter.write(data, writeSheet);}executor.shutdown();} catch (Exception e) {throw new RuntimeException("导出失败", e);}}
3.2.2 getTotalCount

获取SQL查询结果的总记录数,用于计算分页信息。

java">private int getTotalCount(String originalSql) {String countSql = "SELECT COUNT(*) FROM (" + originalSql + ")";return jdbcTemplate.queryForObject(countSql, new Object[]{}, Integer.class);}
3.2.3 QueryTask

实现了Callable接口,用于异步执行分页查询任务。

java">@AllArgsConstructor
private static class QueryTask implements Callable<List<?>> {private final JdbcTemplate jdbcTemplate;private final String originalSql;private final Class<?> entityClass;private final int startRow;private final int endRow;@Overridepublic List<?> call() throws Exception {String paginatedSql = "SELECT * FROM (SELECT ROWNUM rn, temp.* FROM (" + originalSql + ") temp WHERE ROWNUM <= ?) WHERE rn > ?";return jdbcTemplate.query(paginatedSql, new Object[]{endRow, startRow}, new BeanPropertyRowMapper<>(entityClass));}
}

4. 关键点说明

4.1 多线程优化

通过创建固定大小的线程池(ExecutorService),可以并发地执行多个分页查询任务,从而显著减少整体导出时间。每个任务都是一个QueryTask实例,负责从数据库中获取指定范围的数据。

4.2 分页查询

为了避免一次性加载过多数据导致内存溢出,采用了分页查询的方式。每次只查询一页的数据,并将其写入到Excel文件中。

4.3 异常处理

在整个导出过程中,对可能出现的异常进行了捕获和处理,确保即使发生错误也能给出明确提示。

5. 总结

本文介绍了如何使用EasyExcel库结合多线程技术实现高效的数据导出功能。通过合理的分页查询策略和多线程并发执行,不仅提高了导出效率,还保证了系统的稳定性和可靠性。希望这篇文章能够帮助你在实际项目中更好地解决类似问题。


附录

附录A: 依赖管理

pom.xmlbuild.gradle中添加以下依赖:

<!-- pom.xml -->
<dependency> 
<groupId>com.alibaba</groupId> 
<artifactId>easyexcel</artifactId> 
<version>最新版本</version> </dependency> 
<dependency> 
<groupId>org.springframework</groupId> 
<artifactId>spring-jdbc</artifactId> 
<version>最新版本</version> 
</dependency>
// build.gradle 
implementation 'com.alibaba:easyexcel:最新版本' 
implementation 'org.springframework:spring-jdbc:最新版本'

附录B: SQL注意事项

  • 确保SQL查询语句的正确性,特别是分页查询部分。
  • 对于不同数据库,分页语法可能有所不同,请根据实际情况调整。

附录C: 性能调优建议

  • 根据服务器资源情况,合理配置线程池大小。
  • 考虑使用更高效的批量插入方式,如EasyExcel提供的批量写入功能。

希望这份文档对你有所帮助!如果有任何问题或建议,请随时联系我。


http://www.ppmy.cn/server/169684.html

相关文章

Ubuntu搭建RTSP服务器

下载 http://www.live555.com/liveMedia/public/ 安装ffmpeg sudo apt install -y ffmpeg 转换文件&#xff08;必须&#xff01;&#xff09; ffmpeg -i test.mp4 -codec copy -bsf: h264_mp4toannexb -f h264 test.264编译 ./genMakefiles linux-64bit make 启动服务器…

DeepSeek R1 与 OpenAI O1:机器学习模型的巅峰对决

我的个人主页 我的专栏&#xff1a;人工智能领域、java-数据结构、Javase、C语言&#xff0c;希望能帮助到大家&#xff01;&#xff01;&#xff01;点赞&#x1f44d;收藏❤ 一、引言 在机器学习的广袤天地中&#xff0c;大型语言模型&#xff08;LLM&#xff09;无疑是最…

uniapp 打包安卓 集成高德地图

接入高德地图 let vm this;uni.chooseLocation({success: function (res) {// console.log(位置名称&#xff1a; res.name);// console.log(详细地址&#xff1a; res.address);// console.log(纬度&#xff1a; res.latitude);// console.log(经度&#xff1a; res.long…

实验流量统计设计

当我们需要统计实验中每个分支的实际进入次数时&#xff0c;如何设计一个高效、可靠且对业务影响最小的方案&#xff0c;成为了关键。以下是几种常见的流量统计方案的分析与实现设计 目标 不影响实际业务使用&#xff0c;不应该因为汇报错误&#xff0c;导致灰度、甚至实际业…

【Java从入门到起飞】流程控制语句

文章目录 1. 顺序结构2. 分支语句2.1 if-else条件判断结构2.1.1 基本语法2.1.3 if...else嵌套2.1.4 其它说明 2.2 switch-case选择结构2.2.1 基本语法2.2.3 利用case的穿透性2.2.4 if-else语句与switch-case语句比较 3. 循环语句3.1 for循环3.1.1 基本语法 3.2 while循环3.2.1 …

《深度学习》——RNN网络简单介绍

文章目录 RNN网络简介工作原理网络结构训练方法应用领域 RNN网络简介 循环神经网络&#xff08;Recurrent Neural Network&#xff0c;RNN&#xff09;是一种专门用于处理序列数据的神经网络&#xff0c;在自然语言处理、语音识别、时间序列预测等领域有广泛应用。 RNN 是一种…

高等数学(上)题型笔记(六)定积分的应用

目录 1 三角函数定积分的结论 2 定积分的微元法&#xff08;元素法&#xff09; 2.1 使用条件 2.2 使用步骤 3 定积分的几何应用 3.1 平面图形的面积 3.1.1 直角坐标系的情形 3.1.1.1 X型 3.1.1.2 Y型 3.1.1.3 双型 3.1.1.4 复合&#xff1a;分割型 3.1.1.5 引入参…

AI客服-接入deepseek大模型到微信(本地部署deepseek集成微信自动收发消息)

1.本地部署 1.1 ollama Ollama软件通过其高度优化的推理引擎和先进的内存管理机制&#xff0c;显著提升了大型语言模型在本地设备上的运行效率。其核心采用了量化技术&#xff08;Quantization&#xff09;以降低模型的计算复杂度和存储需求&#xff0c;同时结合张量并行计算&…