大批量数据导出csv,平替导出excel性能优化解决方案封装工具类
阿丹:
有些业务逻辑需要在导出非常大量的数据,几百甚至几千万的数据这个时候再导出excel来对于性能都不是很友好,这个时候就需要替换实现思路来解决这个问题。
本文章提供了两种解决的方案,也是两种从数据库中拿取数据的方式一种是原生的jdbc一种是使用mybatis来封装对象来完成的。
使用字符串数组的导出:
package com.lianlu.export.util;import org.apache.poi.ss.formula.functions.T;import java.io.*;
import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;/*** CSV导出工具类,用于将数据列表导出为CSV文件。*/
public class CSVExportUtil {/*** 导出CSV文件。** @param dataList 需要导出的内容,类型为字符串数组的列表。* @param validationRulesMap 校验和替换规则的映射,键为列索引,值为一个映射,其中键为需要替换的字符串,值为替换后的字符串。* @param headers CSV文件的表头,类型为字符串数组。* @param fileName 导出CSV文件的名称。* @throws IOException 如果在写入文件过程中发生异常。*/public static void exportCSV(List<String[]> dataList, Map<Integer, Map<String, String>> validationRulesMap, String[] headers, String fileName) throws IOException {// 预处理数据(校验和替换)List<String[]> preprocessedDataList = preprocessData(dataList, validationRulesMap);// 写入CSV文件writeCSVToFile(preprocessedDataList, headers, fileName);}/*** 不需要替换规则的导出* @param dataList* @param headers* @param fileName* @throws IOException*/public static void exportCSV(List<String[]> dataList, String[] headers, String fileName) throws IOException {// 写入CSV文件writeCSVToFile(dataList, headers, fileName);}/*** 预处理数据列表(校验和替换)。** @param dataList 原始数据列表。* @param validationRulesMap 校验和替换规则的映射。* @return 预处理后的数据列表。*/private static List<String[]> preprocessData(List<String[]> dataList, Map<Integer, Map<String, String>> validationRulesMap) {return dataList.stream().map(row -> preprocessDataRow(row, validationRulesMap)).collect(Collectors.toList());}/*** 预处理单行数据(校验和替换)。** @param row 单行数据。* @param validationRulesMap 校验和替换规则的映射。* @return 预处理后的单行数据。*/private static String[] preprocessDataRow(String[] row, Map<Integer, Map<String, String>> validationRulesMap) {for (Map.Entry<Integer, Map<String, String>> entry : validationRulesMap.entrySet()) {int columnIndex = entry.getKey();Map<String, String> rules = entry.getValue();String originalValue = row[columnIndex];String replacedValue = rules.getOrDefault(originalValue, originalValue);row[columnIndex] = replacedValue;}return row;}/*** 将预处理后的数据写入CSV文件。** @param dataList 预处理后的数据列表。* @param headers CSV文件的表头。* @param fileName 导出CSV文件的名称。* @throws IOException 如果在写入文件过程中发生异常。*/private static void writeCSVToFile(List<String[]> dataList, String[] headers, String fileName) throws IOException {try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) {// 写入表头writer.write(String.join(",", headers));writer.newLine();// 分批写入数据以提高性能AtomicInteger counter = new AtomicInteger(0);while (counter.get() < dataList.size()) {int batchSize = Math.min(10000, dataList.size() - counter.get()); // 每次写入10000条数据,可根据实际需求调整List<String[]> batchData = dataList.subList(counter.get(), counter.get() + batchSize);for (String[] dataRow : batchData) {writer.write(String.join(",", dataRow));writer.newLine();}counter.addAndGet(batchSize);}}}/*** 从泛型对象中获取属性值并转换为字符串数组。** @param data 泛型对象* @return 字符串数组,包含对象的属性值*/private String[] convertObjectToArray(T data) {Class<?> clazz = data.getClass();Field[] fields = clazz.getDeclaredFields();// 获取对象的所有字段,并设置它们为可访问for (Field field : fields) {field.setAccessible(true);}String[] rowData = new String[fields.length];// 遍历所有字段,获取每个字段的值并添加到字符串数组中for (int i = 0; i < fields.length; i++) {try {rowData[i] = fields[i].get(data).toString();} catch (IllegalAccessException e) {throw new RuntimeException("Failed to access field value", e);}}return rowData;}}
通过对象的导出:
package com.lianlu.export.util;import java.io.*;
import java.lang.reflect.Field;
import java.util.*;public class CSVExportUtil<T> {/*** 导出CSV文件。** @param dataList 需要导出的数据列表(泛型对象列表)* @param validationRulesMap 校验和替换规则映射(键为列索引,值为校验和替换规则的映射)* @param headers CSV表头数组* @param fileName 导出CSV文件的名称* @throws IOException 如果在写入文件时发生错误*/public void exportCSV(List<T> dataList, Map<Integer, Map<String, String>> validationRulesMap, String[] headers, String fileName) throws IOException {// 预处理数据(校验和替换)List<String[]> preprocessedData = preprocessData(dataList, validationRulesMap);// 写入CSV文件writeCSV(preprocessedData, headers, fileName);}/*** 预处理数据(校验和替换)。** @param dataList 数据列表(泛型对象列表)* @param validationRulesMap 校验和替换规则映射(键为列索引,值为校验和替换规则的映射)* @return 预处理后的数据(字符串数组列表)*/private List<String[]> preprocessData(List<T> dataList, Map<Integer, Map<String, String>> validationRulesMap) {List<String[]> preprocessedData = new ArrayList<>(dataList.size());for (T data : dataList) {String[] rowData = convertObjectToArray(data);preprocessedData.add(validateAndReplace(rowData, validationRulesMap));}return preprocessedData;}/*** 从泛型对象中获取属性值并转换为字符串数组。** @param data 泛型对象* @return 字符串数组,包含对象的属性值*/private String[] convertObjectToArray(T data) {Class<?> clazz = data.getClass();Field[] fields = clazz.getDeclaredFields();// 获取对象的所有字段,并设置它们为可访问for (Field field : fields) {field.setAccessible(true);}String[] rowData = new String[fields.length];// 遍历所有字段,获取每个字段的值并添加到字符串数组中for (int i = 0; i < fields.length; i++) {try {rowData[i] = fields[i].get(data).toString();} catch (IllegalAccessException e) {throw new RuntimeException("Failed to access field value", e);}}return rowData;}/*** 根据提供的校验和替换规则对字符串数组进行校验和替换。** @param rowData 字符串数组* @param rulesMap 校验和替换规则映射(键为列索引,值为校验和替换规则的映射)* @return 校验和替换后的字符串数组*/private String[] validateAndReplace(String[] rowData, Map<Integer, Map<String, String>> rulesMap) {for (Map.Entry<Integer, Map<String, String>> entry : rulesMap.entrySet()) {int columnIndex = entry.getKey();Map<String, String> ruleMap = entry.getValue();String currentValue = rowData[columnIndex];if (ruleMap.containsKey(currentValue)) {rowData[columnIndex] = ruleMap.get(currentValue);}}return rowData;}/*** 将预处理后的数据写入CSV文件。** @param preprocessedData 预处理后的数据(字符串数组列表)* @param headers CSV表头数组* @param fileName 导出CSV文件的名称* @throws IOException 如果在写入文件时发生错误*/private void writeCSV(List<String[]> preprocessedData, String[] headers, String fileName) throws IOException {try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) {CSVWriter csvWriter = new CSVWriter(writer);// 写入表头csvWriter.writeNext(headers);// 写入数据csvWriter.writeAll(preprocessedData);csvWriter.close();}}
}
相关文章:
大批量数据导出csv,平替导出excel性能优化解决方案封装工具类
阿丹: 有些业务逻辑需要在导出非常大量的数据,几百甚至几千万的数据这个时候再导出excel来对于性能都不是很友好,这个时候就需要替换实现思路来解决这个问题。 本文章提供了两种解决的方案,也是两种从数据库中拿取数据的方式一种是…...

C++ Qt开发:Charts绘制各类图表详解
Qt 是一个跨平台C图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍TreeWidget与QCharts的常用方法及灵活运用。 …...

【SassVue】仿网易云播放器动画
简介 仿网易云播放动画 效果图(效果图) 最终成品效果 动画组件 src/components/music/MusicPlayAnimate.vue <template><div class"music-play"><div></div><div></div><div></div></di…...

CentOS进入单用户模式
一、重启 二、出现内核选项 按“e” 三、编辑这一行 输入 rw init/sysroot/bin/sh 四、进入单用户模式 ctrlx 进入 五、切换目录 chroot /sysroot 六、然后你就操作你的系统了。 修改密码等等...

微信小程序~如何设置页面的背景色
微信小程序~如何设置页面的背景色 众所周知,微信小程序每个页面由.json,.scss,.ts,.wxml这四个文件组成。 有的小伙伴会发现,需要给页面加背景色的时候,只需在此页面的.scss文件中写个page{background-colo…...

图灵日记之java奇妙历险记--输入输出方法数组
目录 输入输出输出到控制台从键盘输入使用 Scanner 读取字符串/整数/浮点数使用 Scanner 循环读取 猜数字方法方法定义方法调用的执行过程实参和形参的关系(重要)方法重载 数组数组的创建数组的初始化动态初始化静态初始化 数组的使用元素访问遍历数组 数组是引用类型null数组应…...

CSS新手入门笔记整理:CSS3弹性盒模型
特点 子元素宽度之和小于父元素宽度,所有子元素最终的宽度就是原来定义的宽度。子元素宽度之和大于父元素宽度,子元素会按比例来划分宽度。在使用弹性盒子模型之前,必须为父元素定义“display:flex;”或“display:inline-flex;”。 弹性盒子…...

OCP NVME SSD规范解读-1
OCP(Open Compute Project)是一个由Facebook于2011年发起的开源项目。其目标是重新设计和优化数据中心的硬件,包括服务器、存储、网络设备等,以提高效率,降低运营成本,并推动技术的创新和标准化。 在OCP中&…...
大规模和复杂问题挑战——分治思想来应战
分治思想利用了问题的内在结构和性质,使得大规模和复杂的问题能够被有效地解决。具体来说,分治思想的本质是通过问题分解、递归处理和解的合并,将一个复杂问题转化为一系列更简单的子问题,并最终得到原问题的解。 1、分治思想的本…...

六西格玛的科技漩涡——张驰咨询如何促成企业变革
在管理的海洋里,六西格玛管理是一艘稳健的航船,在质量管理的汪洋中乘风破浪,尽管质疑之声像远处的风暴不断逼近,但张驰咨询公司依靠这艘航船坚持初心,驭风而行。 20载耕耘,张驰咨询不仅仅是培养了超过8000…...

由于被认为是客户端对错误(例如:畸形的请求语法、无效的请求信息帧或者虚拟的请求路由),服务器无法或不会处理当前请求。
问题描述: 由于被认为是客户端对错误(例如:畸形的请求语法、无效的请求信息帧或者虚拟的请求路由),服务器无法或不会处理当前请求。 在实现向数据库中添加记录时,请求发送无效,参数也未传递到控…...

【案例】图片预览
效果图 如何让图片放大,大多数的UI组件都带有这种功能,今天给大家介绍的这个插件除了放大之外,还可以旋转、移动、翻转、旋转、二次放大(全屏) 实现 npm i v-viewer -Smain.js 中引入 import viewerjs/dist/viewer.c…...
ubuntu 18/20/22 安装 mysql 数据库
这里写自定义目录标题 ubuntu 18/20/22 安装 mysql 数据库1. 准备2. 安装 mysql3. 配置4. 测试 demo 用户5 服务管理5.1 查看服务状态5.2 启动服务5.3 停止服务5.4 重启服务 ubuntu 18/20/22 安装 mysql 数据库 1. 准备 安装前需要知道 root 用户的密码 假如不知道 root 用户…...

通过U盘:将电脑进行重装电脑
目录 一.老毛桃制作winPE镜像 1.制作准备 2.具体制作 下载老毛桃工具 插入U盘 选择制作模式 正式配置U盘 安装提醒 安装成功 具体操作 二.使用ultrasio制作U盘 1.具体思路 2.图片操作 三.硬盘安装系统 具体操作 示例图 编辑 一.老毛桃制作winPE镜像 1.制作准…...

C# SqlSugar 数据库 T4模板
生成效果 模板代码 <# template debug"false" hostspecific"true" language"C#" #> <# output extension".cs" #> <# assembly name"System.Core" #> <# assembly name"System.Data" #>…...
ARM AArch64的TrustZone架构详解(下)
目录 五、软件架构 5.1 顶层软件架构 5.2 信任消息(message) 5.3 调度 5.4 OPTEE...

《Nature》预测 2024 科技大事:GPT-5预计明年发布等
《Nature》杂志近日盘点了 2024 年值得关注的科学事件,包括 GPT-5 与新一代 AlphaFold、超算 Jupiter、探索月球任务、生产「超级蚊子」、朝向星辰大海、试验下一代新冠疫苗、照亮暗物质、意识之辩第二回合、应对气候变化。 今年以来,以 ChatGPT 为代表…...

「Verilog学习笔记」并串转换
专栏前言 本专栏的内容主要是记录本人学习Verilog过程中的一些知识点,刷题网站用的是牛客网 串并转换操作是非常灵活的操作,核心思想就是移位。串转并就是把1位的输入放到N位reg的最低位,然后N位reg左移一位,在把1位输入放到左移后…...
应急响应常用命令
应急响应的基本思路 a. 收集信息:收集告警信息、客户反馈信息、设备主机信息等 b. 判断类型:安全事件类型判断。(钓鱼邮件、Webshll、爆破、中毒等) c. 控制范围:隔离失陷设备 d. 分析研判:根据收集回来的…...
使用React和ResizeObserver实现自适应ECharts图表
关键词 React ECharts ResizeObserver 摘要 在现代 Web 开发中,响应式布局和数据可视化是非常常见的需求。本文将介绍如何使用React、ResizeObserver和ECharts库来创建一个自适应的图表组件。 什么是ResizeObserver ResizeObserver是JavaScript的一个API&#x…...

接口测试中缓存处理策略
在接口测试中,缓存处理策略是一个关键环节,直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性,避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明: 一、缓存处理的核…...
python如何将word的doc另存为docx
将 DOCX 文件另存为 DOCX 格式(Python 实现) 在 Python 中,你可以使用 python-docx 库来操作 Word 文档。不过需要注意的是,.doc 是旧的 Word 格式,而 .docx 是新的基于 XML 的格式。python-docx 只能处理 .docx 格式…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...
Fabric V2.5 通用溯源系统——增加图片上传与下载功能
fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...
HTML前端开发:JavaScript 获取元素方法详解
作为前端开发者,高效获取 DOM 元素是必备技能。以下是 JS 中核心的获取元素方法,分为两大系列: 一、getElementBy... 系列 传统方法,直接通过 DOM 接口访问,返回动态集合(元素变化会实时更新)。…...

恶补电源:1.电桥
一、元器件的选择 搜索并选择电桥,再multisim中选择FWB,就有各种型号的电桥: 电桥是用来干嘛的呢? 它是一个由四个二极管搭成的“桥梁”形状的电路,用来把交流电(AC)变成直流电(DC)。…...

【若依】框架项目部署笔记
参考【SpringBoot】【Vue】项目部署_no main manifest attribute, in springboot-0.0.1-sn-CSDN博客 多一个redis安装 准备工作: 压缩包下载:http://download.redis.io/releases 1. 上传压缩包,并进入压缩包所在目录,解压到目标…...
Cursor AI 账号纯净度维护与高效注册指南
Cursor AI 账号纯净度维护与高效注册指南:解决限制问题的实战方案 风车无限免费邮箱系统网页端使用说明|快速获取邮箱|cursor|windsurf|augment 问题背景 在成功解决 Cursor 环境配置问题后,许多开发者仍面临账号纯净度不足导致的限制问题。无论使用 16…...
[QMT量化交易小白入门]-六十二、ETF轮动中简单的评分算法如何获取历史年化收益32.7%
本专栏主要是介绍QMT的基础用法,常见函数,写策略的方法,也会分享一些量化交易的思路,大概会写100篇左右。 QMT的相关资料较少,在使用过程中不断的摸索,遇到了一些问题,记录下来和大家一起沟通,共同进步。 文章目录 相关阅读1. 策略概述2. 趋势评分模块3 代码解析4 木头…...