Java一次性查询几十万 几百万数据解决办法

在做大数据量同步的时候,需要注意的内存使用问题,程序稍微控制不足,可能就会导致内存溢出等问题...在网上找了一些资料,发现大家都使用的如下方式:

 

1、先批量查询出所有数据,例子中是一万条一次

2、在查出数据后把每次的数据按一定的规则存入本地文件

3、读取文件数据时,可以采取单例模式,批量提交等方式操作数据库

 

代码如下:

public boolean createUploadFileByAll() {
        boolean flag = false;
        int onerun = 10000;  // 每次读取记录数
        int lastrow = 0; // 每次读取后的最后一条记录
        ResultSet rs = null;
        Statement stat = null;
        try {
            String hql = "SELECT count(*) FROM T_jgdm";
            int datanum = ((Long) this.getSession().createQuery(hql).uniqueResult()).intValue();
            int runnum = datanum % onerun == 0 ? (datanum / onerun) : (datanum / onerun) + 1;
            // 分批读取数据
            conn = getConn();
            stat = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
            StringBuffer text = new StringBuffer();
            text.append("机构代码(JGDM)-").append("机构名称(JGMC)-").append("首次办证日期(SCBZRQ)-");
            text.append("外经委填表人(WJWTBR)-").append("可否公开(GK)-").append("发卡标志(FKBZ)-");
            text.append("发卡数量(FKSL)-").append("打印标志(DYBZ)-").append("电邮地址(EMAIL)-");
            text.append("网址(URL)-").append("最后修改日期(LASTDATE)-").append("操作标志(CZFLAG)-");
            text.append("是否邮寄(YJFLAG)-").append("数据状态(SJZT)-").append("检验结果(JYJG)-");
            text.append("分值(FZ)-").append("作废日期(ZFRQ)-").append("办证机构区划代码(BZJGDM)-");
            text.append("变更日期(BGRQ)-").append("年检日期(NJRQ)-").append("年检期限(NJQX)-");
            text.append("迁址日期(QZRQ)-").append("开户银行(KHYH)-").append("开户账号(KHZH)\r\n");  // 文件列注解  \r\n为换行符

            for (int r = 0; r < runnum; r++) {
                System.out.println("createUploadFileByAll--" + datanum + " 开始查询第" + (r + 1) + "批数据");
                String sql = "SELECT *\n" +
                        "  FROM (SELECT rownum rn, " + QUERY_FIELD + " FROM t_jgdm ORDER BY rownum ASC)\n" +
                        " WHERE rn > " + lastrow;
                stat.setMaxRows(onerun);
                stat.setFetchSize(1000); // 当调用rs.next时,ResultSet会一次性从服务器上取1000行数据回来,在下次rs.next时,它可以直接从内存中获取出数据而不需要网络交互,从而提高效率
                rs = stat.executeQuery(sql);
                // 生成文件
                int i = 1;
                while (rs.next()) {
                    String jgdm = rs.getString("JGDM");
                    String jgmc = rs.getString("JGMC");
                    String scbzrq = rs.getString("SCBZRQ");
                    String wjwtbr = rs.getString("WJWTBR");
                    String gk = rs.getString("GK");
                    String fkbz = rs.getString("FKBZ");
                    String fksl = rs.getString("FKSL");
                    String dybz = rs.getString("DYBZ");
                    String email = rs.getString("EMAIL");
                    String url = rs.getString("URL");
                    String lastdate = rs.getString("LASTDATE");
                    String czflag = rs.getString("CZFLAG");
                    String yjflag = rs.getString("YJFLAG");
                    String sjzt = rs.getString("SJZT");
                    String jyjg = rs.getString("JYJG");
                    String fz = rs.getString("FZ");
                    String zfrq = rs.getString("ZFRQ");
                    String bzjgdm = rs.getString("BZJGDM");
                    String bgrq = rs.getString("BGRQ");
                    String njrq = rs.getString("NJRQ");
                    String njqx = rs.getString("NJQX");
                    String qzrq = rs.getString("QZRQ");
                    String khyh = rs.getString("KHYH");
                    String khzh = rs.getString("KHZH");

                    text.append(StringUtil.isNotEmpty(jgdm) ? jgdm : " ").append("^^^^");
                    text.append(StringUtil.isNotEmpty(jgmc) ? jgmc : " ").append("^^^^");
                    text.append(StringUtil.isNotEmpty(scbzrq) ? scbzrq : " ").append("^^^^");
                    text.append(StringUtil.isNotEmpty(wjwtbr) ? wjwtbr : " ").append("^^^^");
                    text.append(StringUtil.isNotEmpty(gk) ? gk : " ").append("^^^^");
                    text.append(StringUtil.isNotEmpty(fkbz) ? fkbz : " ").append("^^^^");
                    text.append(StringUtil.isNotEmpty(fksl) ? fksl : "").append("^^^^");
                    text.append(StringUtil.isNotEmpty(dybz) ? dybz : " ").append("^^^^");
                    text.append(StringUtil.isNotEmpty(email) ? email : " ").append("^^^^");
                    text.append(StringUtil.isNotEmpty(url) ? url : " ").append("^^^^");
                    text.append(StringUtil.isNotEmpty(lastdate) ? lastdate : "").append("^^^^");
                    text.append(StringUtil.isNotEmpty(czflag) ? czflag : " ").append("^^^^");
                    text.append(StringUtil.isNotEmpty(yjflag) ? yjflag : " ").append("^^^^");
                    text.append(StringUtil.isNotEmpty(sjzt) ? sjzt : " ").append("^^^^");
                    text.append(StringUtil.isNotEmpty(jyjg) ? jyjg : " ").append("^^^^");
                    text.append(StringUtil.isNotEmpty(fz) ? fz : " ").append("^^^^");
                    text.append(StringUtil.isNotEmpty(zfrq) ? zfrq : " ").append("^^^^");
                    text.append(StringUtil.isNotEmpty(bzjgdm) ? bzjgdm : " ").append("^^^^");
                    text.append(StringUtil.isNotEmpty(bgrq) ? bgrq : " ").append("^^^^");
                    text.append(StringUtil.isNotEmpty(njrq) ? njrq : " ").append("^^^^");
                    text.append(StringUtil.isNotEmpty(njqx) ? njqx : " ").append("^^^^");
                    text.append(StringUtil.isNotEmpty(qzrq) ? qzrq : " ").append("^^^^");
                    text.append(StringUtil.isNotEmpty(khyh) ? khyh : " ").append("^^^^");
                    text.append(StringUtil.isNotEmpty(khzh) ? khzh : " ").append("^^^^\r\n");

                    if (i % 1000 == 0) {
                        // 创建文件,并追加内容
                        FileOperate fileOperate = FileOperate.getFileOperate();
                        fileOperate.createFileByAddTo(UPLOAD_TEMP_FILE_NAME_BY_ALL, text.toString());
                        text = new StringBuffer("");
                    }
                    i++;
                }
                // 向文件中追加不能被1000求余的内容
                if (text.length() > 10) {
                    FileOperate fileOperate = FileOperate.getFileOperate();
                    fileOperate.createFileByAddTo(UPLOAD_TEMP_FILE_NAME_BY_ALL, text.toString());
                }
                lastrow += onerun;
            }
            rs.close();
            stat.close();
            conn.close();
            flag = true;
        } catch (Exception e) {
            flag = false;
            e.printStackTrace();
        }
        return flag;
    }

    文件操作方法如下:

/**
     * 新建文件(在原有文件内容上进行追加)
     * 默认编码UTF-8
     * @param filePathAndName
     * @param fileContent
     */
    public void createFileByAddTo(String filePathAndName, String fileContent) {
        try {
            File file = new File(filePathAndName);
            BufferedWriter bw = null;
            try {
                bw = new BufferedWriter(new FileWriter(file, true)); // 参数true表示向文件中追加内容
                bw.write(fileContent);
                bw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
            System.out.println("新建文件(在原有文件内容上进行追加)出错:");
            e.printStackTrace();
        }
    }

 

<think>好的,我现在需要帮用户解决如何使用EasyExcel生成包含几十万数据的大XLSX文件的问题。用户之前可能已经了解过EasyExcel的优势,比如内存优化,但现在需要具体的实现步骤和最佳实践。首先,我得回顾一下用户提供的引用内容,确保回答符合他们的需求。 根据引用[1]和[3],EasyExcel在处理大数据时通过重写POI的解析方式,显著降低了内存使用,适合导出百万级数据。用户提到几十万,虽然不算极端,但仍需注意内存管理。引用[2]提到压缩导出时的Stream closed异常,但用户的问题主要是生成单个文件,这部分可能暂时不需要,但可以留作后续扩展。引用[4]和[5]分别涉及模板生成和解析,但用户现在关注的是导出,所以重点在导出流程。 接下来,我需要整理最佳实践的步骤。首先,分页查询数据是关键,避免一次性加载所有数据到内存,这符合引用[3]中的分页处理建议。然后,使用EasyExcel的WriteSheet和异步写入功能,这能进一步减少内存占用。另外,临时文件的使用也很重要,因为直接写入流可能在网络中断时失败,而临时文件可以确保数据完整性。 在代码示例部分,需要展示如何分页查询,配置ExcelWriter,以及逐页写入数据。同时,要提到模板导出和样式优化,比如禁用默认样式和设置自动列宽,这些都能提升性能和用户体验。最后,异常处理和资源清理也是必要步骤,防止资源泄漏。 可能还需要考虑用户的其他潜在问题,比如如何处理特殊数据类型,或者如何优化查询速度。不过根据当前问题,重点应放在导出流程和内存管理上。需要确保代码示例正确,并解释每一步的作用,让用户清楚最佳实践的原因。另外,引用相关的引用文献作为支持,比如引用[1]和[3]来强调内存优化的有效性。</think>以下是如何使用EasyExcel高效生成包含几十万数据的XLSX文件的逐步解决方案: ### 一、核心实现步骤 1. **分页查询数据** - 采用分段加载策略,每次查询5000-10000数据 - 避免一次性加载全部数据到内存中,防止OOM[^3] ```java public List<DataEntity> queryDataByPage(int pageNo, int pageSize) { // 实际需替换为分页SQL查询 return dataMapper.selectPage((pageNo-1)*pageSize, pageSize); } ``` 2. **配置ExcelWriter** ```java // 使用try-with-resources确保资源释放 try (ExcelWriter excelWriter = EasyExcel.write(fileName, DataEntity.class) .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) .build()) { WriteSheet writeSheet = EasyExcel.writerSheet("数据列表").build(); int pageSize = 10000; for (int pageNo = 1; ; pageNo++) { List<DataEntity> dataChunk = queryDataByPage(pageNo, pageSize); if (dataChunk.isEmpty()) break; excelWriter.write(dataChunk, writeSheet); dataChunk.clear(); // 及时释放内存 } } ``` ### 二、关键优化策略 1. **内存控制技术** - 使用`SXSSF`模式(自动启用) - 默认保留100数据在内存,超出部分写入临时文件[^1] - 调整窗口大小:`.withTempFileWindowSize(500)` 2. **写入方式选择** ```java // 推荐使用异步写入(默认开启) .inMemory(false) // 设置临时文件存储路径 .tempFile(new File("/data/temp")) ``` ### 三、最佳实践建议 1. **模板导出优化** ```java // 复用模板样式(引用[4]) ExcelWriter writer = EasyExcel.write(outStream) .withTemplate(templateFile) .registerWriteHandler(new CellStyleStrategy()) .build(); ``` 2. **性能优化配置** - 禁用默认样式:`.ignoreHeadStyle()` - 关闭自动合并:`.autoTrim(false)` - 设置自动列宽:`.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())` ### 四、异常处理 ```java try { // 写入逻辑 } catch (ExcelGenerateException e) { log.error("Excel生成异常", e); // 清理临时文件 } finally { if (excelWriter != null) { excelWriter.finish(); } // 删除临时文件 } ``` ### 五、扩展方案 1. **海量数据分文件存储** - 每50万生成一个sheet - 超过100万生成新文件[^2] ```java if (totalCount % 1000000 == 0) { excelWriter.finish(); createNewWriter(); } ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值