大批量数据导出csv,平替导出excel性能优化解决方案封装工具类

阿丹:

有些业务逻辑需要在导出非常大量的数据,几百甚至几千万的数据这个时候再导出excel来对于性能都不是很友好,这个时候就需要替换实现思路来解决这个问题。

本文章提供了两种解决的方案,也是两种从数据库中拿取数据的方式一种是原生的jdbc一种是使用mybatis来封装对象来完成的。

使用字符串数组的导出:

java 复制代码
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;
    }





}

通过对象的导出:

java 复制代码
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();
        }
    }
}
相关推荐
q__y__L10 小时前
C#线程同步(二)锁
开发语言·性能优化·c#
瓶子xf11 小时前
Excel制作滑珠图、哑铃图
excel
President~wolf13 小时前
数据处理--生成Excel文档
excel
xuanjiong18 小时前
Excel数据转化为Xmind思维导图全流程(含Word转化格式),实用
excel·xmind
OceanBase数据库官方博客18 小时前
当过滤条件不符合最左前缀时,如何有效利用索引? | OceanBase SQL 优化实践
sql·性能优化·oceanbase·分布式数据库
鼠鼠我捏,要死了捏18 小时前
Spark Shuffle性能优化实践指南:提升大数据处理效率
性能优化·spark·shuffle
切糕师学AI19 小时前
Spire.XLS for .NET 中, 将 Excel 转换为 PDF 时, 如何设置纸张大小为A4纸,并将excel内容分页放置?
pdf·.net·excel·spire
qq_5469372719 小时前
Excel文件批量加密工具
excel
Alexon Xu1 天前
ThreadLocal总结
性能优化
玥老师1 天前
Excel超级处理器,多个word文档中按字体颜色提取到Excel表格中
excel