java以流的方式分页读取sql结果并写为excel
时间: 2025-06-30 11:00:17 浏览: 5
### Java程序以流的方式分页读取SQL查询结果并导出至Excel
#### 使用Apache POI和EasyExcel实现最佳实践
为了高效处理大量数据,建议采用流式处理的方法。这不仅能够减少内存占用,还能提高系统的响应速度。对于Java应用程序来说,可以利用`JDBC`的滚动游标特性来逐行获取数据库记录,并通过Apache POI或EasyExcel库将这些记录实时写入到Excel文件中。
#### 数据库连接设置与预编译语句创建
建立持久化层时,应优先考虑使用Spring JPA或其他ORM框架简化操作流程;但如果追求极致性能,则可以直接借助原生JDBC API完成任务。下面展示了一个简单的例子,展示了如何配置数据库连接池以及编写带有参数化的SELECT SQL语句:
```java
// 加载驱动类
Class.forName("com.mysql.cj.jdbc.Driver");
// 创建DataSource对象
HikariConfig config = new HikariConfig();
config.setDriverClassName("com.mysql.cj.jdbc.Driver");
config.setJdbcUrl("jdbc:mysql://localhost:3306/testdb?useSSL=false&serverTimezone=UTC");
config.setUsername("root");
config.setPassword("password");
HikariDataSource dataSource = new HikariDataSource(config);
// 获取Connection对象
try (Connection conn = dataSource.getConnection()) {
String sql = "SELECT * FROM users LIMIT ? OFFSET ?";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
int pageSize = 1000;
long totalRows = getTotalRowCount(conn); // 自定义函数计算总行数
for (int offset = 0; offset < totalRows; offset += pageSize) {
pstmt.setInt(1, pageSize);
pstmt.setLong(2, offset);
processResultSet(pstmt.executeQuery(), excelWriter); // 处理每一页的数据
Thread.sleep(50L); // 防止过快执行导致资源耗尽
}
closeQuietly(excelWriter); // 关闭Excel Writer
} catch (SQLException | InterruptedException e) {
throw new RuntimeException(e.getMessage());
}
}
```
上述代码片段实现了基于MySQL数据库的一个简单案例[^1]。这里采用了HikariCP作为连接池解决方案,它具有出色的吞吐量表现,在高并发场景下尤为适用。同时注意到了防止因频繁请求造成的服务器压力过大问题,因此加入了短暂休眠机制。
#### 流式写出Excel文档
针对不同规模的数据集可以选择不同的技术栈来进行优化。如果预计要处理非常庞大的数据集合(比如超过十万甚至百万级别),则推荐使用阿里巴巴开源项目——EasyExcel替代传统的Apache POI。因为前者专门针对大数据量做了很多针对性改进措施,如支持SAX解析模式、提供更灵活高效的API接口等优点[^3]。
下面是具体的应用示例,说明了怎样结合前面提到的技术方案构建一个完整的应用逻辑链路:
```java
public void exportLargeDataToExcel(HttpServletResponse response) throws IOException {
List<List<String>> headList = Arrays.asList(
Arrays.asList("ID", "Name", "Email")
);
ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
.head(headList)
.build();
Sheet sheet = new Sheet(1, 0);
sheet.setSheetName("User Data");
Connection connection = null;
try {
Class.forName("com.mysql.cj.jdbc.Driver");
DataSource ds = getDataSource(); // 方法需自行实现
connection = ds.getConnection();
PreparedStatement psmt = connection.prepareStatement(
"SELECT id,name,email FROM user ORDER BY id ASC LIMIT ? OFFSET ?");
int batchSize = 1000;
Long totalCount = getUserCount(connection); // 方法需自行实现
for (long i = 0; i < totalCount / batchSize + 1 ; ++i){
psmt.setInt(1,batchSize );
psmt.setLong(2,i*batchSize);
ResultSet rs = psmt.executeQuery();
while(rs.next()){
writeRowData(sheet ,rs.getString("id"),rs.getString("name"),
rs.getString("email"));
}
rs.close();
}
psmt.close();
} finally {
if(null != connection && !connection.isClosed()){
connection.close();
}
}
excelWriter.finish();
}
private static void writeRowData(Sheet sheet,Object... args){
Row row = sheet.createRow(sheet.getLastRowNum() + 1);
CellStyle cellStyle = sheet.getWorkbook().createCellStyle();
CreationHelper createHelper = sheet.getWorkbook().getCreationHelper();
Font font = sheet.getWorkbook().createFont();
for(int j = 0;j<args.length;++j){
Cell cell = row.createCell(j);
cell.setCellValue(String.valueOf(args[j]));
cell.setCellStyle(cellStyle);
}
}
```
此段代码充分体现了面向接口编程的思想,使得整个过程更加模块化易于维护扩展。另外值得注意的是,为了避免一次性加载过多数据造成OOM错误的发生,每次只取出固定数量(`batchSize`) 的记录进行加工转换后再释放掉对应的资源。
阅读全文
相关推荐


















