该表数据有一百多万条,三十多个字段。需求是根据机构编码分组后输出excel文档,文档名称为机构名称,虽然datagrip可以直接导出为excel表格,但是该表根据条件分组之后仍有一百多组,所以直接用程序跑更方便。
这里连接的是oracle数据库,亦菲彦祖们记得修改为自己连接的数据库格式
SAVE_FOLDER_PATH 需要在对应路径下先创建一个data文件夹(当然亦菲彦祖们也可以在代码自行添加创建文件夹逻辑)
其它地方应该没有需要注意的了。
废话不说,直接上代码
public class ExcelTest {
private static final Logger LOGGER = LoggerFactory.getLogger(ExcelTest.class);
private static final String URL = "jdbc:oracle:thin:@ip:1521:ORCL";
private static final String USER = "******";
private static final String PASSWORD = "******";
private static final String SAVE_FOLDER_PATH = "D:/***/****/****/data/";
private static final String[] HEADERS = {"***","***",....};
private static final String SELECT_FIXMEDINS_NAMES_SQL = "******";
public static void main(String[] args) {
Map<String, String> codeNameMap = getUniqueFIXMEDINSNames(URL, USER, PASSWORD);
// codeNameMap.forEach((code, name) -> exportToExcel(code, name, URL, USER, PASSWORD));
//并行处理
codeNameMap.entrySet().parallelStream().forEach(entry -> exportToExcel(entry.getKey(), entry.getValue(), URL, USER, PASSWORD));
}
public static Map<String, String> getUniqueFIXMEDINSNames(String url, String user, String password) {
Map<String, String> codeNameMap = new LinkedHashMap<>();
try (
Connection connection = getConnection(url, user, password);
PreparedStatement preparedStatement = connection.prepareStatement(SELECT_FIXMEDINS_NAMES_SQL);
ResultSet resultSet = preparedStatement.executeQuery();
) {
while (resultSet.next()) {
String code = resultSet.getString("FIXMEDINS_CODE");
String name = resultSet.getString("FIRST_NAME");
codeNameMap.put(code, name);
}
} catch (SQLException e) {
LOGGER.error("Failed to fetch unique FIXMEDINS names", e);
}
return codeNameMap;
}
public static void exportToExcel(String code, String name, String url, String user, String password) {
try (
Connection connection = getConnection(url, user, password);
PreparedStatement preparedStatement = connection.prepareStatement(EXPORT_SQL);
) {
preparedStatement.setString(1, code);
try (ResultSet resultSet = preparedStatement.executeQuery()) {
Workbook workbook = new SXSSFWorkbook();
//创建工作表
Sheet sheet = workbook.createSheet(name);
createHeaderRow(sheet);
int rowNUm = 1;
while (resultSet.next()) {
//创建新行
Row row = sheet.createRow(rowNUm++);
//创建单元格并设置值
for (int i = 0; i < HEADERS.length; i++) {
row.createCell(i).setCellValue(resultSet.getString(i + 1));
}
}
try (FileOutputStream fileOutputStream = new FileOutputStream(SAVE_FOLDER_PATH + sanitizeFileName(name) + ".xlsx")) {
workbook.write(fileOutputStream);
}
// 强制释放 SXSSFWorkbook 临时文件
((SXSSFWorkbook) workbook).dispose();
}
} catch (Exception e) {
LOGGER.error("Failed to export data for institution: " + code, e);
}
}
private static void createHeaderRow(Sheet sheet) {
//创建标题行
Row headerRow = sheet.createRow(0);
//设置列头
for (int i = 0; i < HEADERS.length; i++) {
headerRow.createCell(i).setCellValue(HEADERS[i]);
}
}
private static Connection getConnection(String url, String user, String password) throws SQLException {
return DriverManager.getConnection(URL, USER, PASSWORD);
}
private static String sanitizeFileName(String fileName) {
return fileName.replace("/", "_").replace("\\", "_");
}
}
亦菲彦祖们可以看到在代码中,其实刚开始我是使用的以下代码
codeNameMap.forEach((code, name) -> exportToExcel(code, name, URL, USER, PASSWORD));
进行串行处理的,当然我设置的VM options是-Xmx8192m(开始使用的是IDEA默认的,导致直接报oom了)
使用VisualVM分析可以看到还是能接受的,差不多四分钟百万多数据就导出成功了
但是使用并行处理可以更快,使用JAVA8引入的特性parallelStream,用
于并行处理流数据。它是 Stream
的一种扩展,旨在充分利用多核 CPU 来加速数据处理。
codeNameMap.entrySet().parallelStream().forEach(entry -> exportToExcel(entry.getKey(), entry.getValue(), URL, USER, PASSWORD));
这样导出数据速度更快了,就是内存飙升的有点高,不过没有什么影响(只进行该操作时)
华丽的分割线~~~~~~~后续需求又遇到问题,所以继续记录下来
如果需要判断执行SQL后对应的resultSet结果集是否为空,可以使用
if(!resultSet.isBeforeFrist()) {
continue;
}
isBeforeFirst()
会在 ResultSet
的第一个数据行之前返回 true
,如果没有数据则返回 false
。
注意啦
resultSet.next()
是用来指示 ResultSet
的光标是否成功移动到下一行,并且返回一个布尔值表示是否还有下一行数据。在读取数据库查询结果时,ResultSet
默认是指向查询结果集的第一行之前的(游标在第一行之前),因此第一次调用 resultSet.next()
会使游标指向第一行数据。每次调用 resultSet.next()
都会将光标向后移动到下一行。
所以千万别使用resultSet.next()对结果集判断是否为空,要不然会少一条数据(本人开始直接用这个进行判断,发现输出的Excel都少了第一条数据,人麻了,踩坑了)
因为如果在while循环之外用resultSet.next()的话,那么while循环中的resultSet.next()就是从第二条开始了。
这就是我遇到的需求和解决的方法,希望对亦菲彦祖们有所帮助!!!
留个赞再走吧~