有些业务逻辑需要在导出非常大量的数据,几百甚至几千万的数据这个时候再导出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 dataList, Map> validationRulesMap, String[] headers, String fileName) throws IOException {
// 预处理数据(校验和替换)
List preprocessedDataList = preprocessData(dataList, validationRulesMap);
// 写入CSV文件
writeCSVToFile(preprocessedDataList, headers, fileName);
}
/**
* 不需要替换规则的导出
* @param dataList
* @param headers
* @param fileName
* @throws IOException
*/
public static void exportCSV(List dataList, String[] headers, String fileName) throws IOException {
// 写入CSV文件
writeCSVToFile(dataList, headers, fileName);
}
/**
* 预处理数据列表(校验和替换)。
*
* @param dataList 原始数据列表。
* @param validationRulesMap 校验和替换规则的映射。
* @return 预处理后的数据列表。
*/
private static List preprocessData(List dataList, Map> validationRulesMap) {
return dataList.stream()
.map(row -> preprocessDataRow(row, validationRulesMap))
.collect(Collectors.toList());
}
/**
* 预处理单行数据(校验和替换)。
*
* @param row 单行数据。
* @param validationRulesMap 校验和替换规则的映射。
* @return 预处理后的单行数据。
*/
private static String[] preprocessDataRow(String[] row, Map> validationRulesMap) {
for (Map.Entry> entry : validationRulesMap.entrySet()) {
int columnIndex = entry.getKey();
Map 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 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 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 {
/**
* 导出CSV文件。
*
* @param dataList 需要导出的数据列表(泛型对象列表)
* @param validationRulesMap 校验和替换规则映射(键为列索引,值为校验和替换规则的映射)
* @param headers CSV表头数组
* @param fileName 导出CSV文件的名称
* @throws IOException 如果在写入文件时发生错误
*/
public void exportCSV(List dataList, Map> validationRulesMap, String[] headers, String fileName) throws IOException {
// 预处理数据(校验和替换)
List preprocessedData = preprocessData(dataList, validationRulesMap);
// 写入CSV文件
writeCSV(preprocessedData, headers, fileName);
}
/**
* 预处理数据(校验和替换)。
*
* @param dataList 数据列表(泛型对象列表)
* @param validationRulesMap 校验和替换规则映射(键为列索引,值为校验和替换规则的映射)
* @return 预处理后的数据(字符串数组列表)
*/
private List preprocessData(List dataList, Map> validationRulesMap) {
List 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> rulesMap) {
for (Map.Entry> entry : rulesMap.entrySet()) {
int columnIndex = entry.getKey();
Map 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 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();
}
}
}