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

本文介绍了一种处理大数据量同步的方法,通过分批查询、限制每次查询的数量并存储到本地文件来避免内存溢出等问题。同时提供了具体的Java实现代码。
在做大数据量同步的时候,需要注意的内存使用问题,程序稍微控制不足,可能就会导致内存溢出等问题...在网上找了一些资料,发现大家都使用的如下方式:

1、先批量查询出所有数据,例子中是一万条一次
2、在查出数据后把每次的数据按一定的规则存入本地文件
3、读取文件数据时,可以采取单例模式,批量提交等方式操作数据库

代码如下:
Java代码
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;
}
文件操作方法如下:
Java代码
/**
* 新建文件(在原有文件内容上进行追加)
* 默认编码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(); } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值