JAVA实现将数据库表中的数据进行分组后,导出为Excel表格

该表数据有一百多万条,三十多个字段。需求是根据机构编码分组后输出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()就是从第二条开始了。

这就是我遇到的需求和解决的方法,希望对亦菲彦祖们有所帮助!!!
留个赞再走吧~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值
OSZAR »