大批量数据导出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…...
日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...
【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...
IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...
使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
