Springboot导出大数据量excel(二)-分页查询数据List<Map>

目录

一、简介

二、测试

1导出100万行数据测试

2exportExcel堆内存

3exportBigExcel堆内存

 三、关键代码

controller

ExcelUtil

源码地址


一、简介

        在上篇文章《Springboot导出大数据量excel》说道查询数据量过大的时候还没到导出的时候就会导致内存溢出(或者占用服务较大内存)的问题。本文内容主要就是解决这个问题。

注意:excel的有最大支持数据行数1048576(2的20次方),超出会报异常:

{"code":500,"msg":"fail","data":"Invalid row number (1048576) outside allowable range (0..1048575)"}

解决方案:

方案一:

根据实际情况拆分成多个excel的压缩包。

方案二:

使用csv(使用wps打开只能看到1048576数据,使用文本工具打开能看到全部数据)。

参考:《Springboot导出大数据量excel(三)-导出csv到压缩包》

二、测试

使用poi框架实现大数量导出测试:

1导出100万行数据测试

jdk 1.8

easypoi 4.4.0

springboot 2.3.0

jvm -Xms128m -Xmx256m

api结果耗时文件大小
ExcelUtil.exportBigExcel
内存溢出62s31.3M
ExcelUtil.exportExcel
内存溢出-

2exportExcel堆内存

内存溢出

3exportBigExcel堆内存

 三、关键代码

controller

@RequestMapping(value = "export3", method = RequestMethod.GET)
    @ApiOperation(value = "导出大量数据(Map)", notes = "SXSSFWorkbook")
    @ApiImplicitParams({})
    public void export3(HttpServletResponse response, HttpServletRequest request) throws Exception {

        Workbook workbook = null;
        Date start = new Date();
        Map<String, Object> params = new HashMap<>();
        params.put("pageSize", 5000);
        workbook = ExcelUtil.exportBigExcel(params, (param, page) -> {
            param.put("pageNum", page);
            //模拟分页查询数据
            List<Map<String, Object>> list = getPageData2(param);
            return list;
        });
        downLoadExcel("test" + System.currentTimeMillis() + ".xlsx", response, workbook);
        System.out.println(new Date().getTime() - start.getTime());
        workbook.close();
    }

ExcelUtil

    public static SXSSFWorkbook exportBigExcel(Map<String, Object> queryParams, MyExcelExportServer server) {
        //这样表示SXSSFWorkbook只会保留100条数据在内存中,其它的数据都会写到磁盘里,这样的话占用的内存就会很少
        SXSSFWorkbook workbook = new SXSSFWorkbook(100);
        //创建Excel的sheet
        SXSSFSheet sheet = workbook.createSheet("sheet1");
        //从list取第一行到最后一行的内容并放到对应的Excel里,若记录里某字段值没有会有问题
        int rowNum = 0;//行数
        int page = 1;
        int var6 = page + 1;

        for (List<Map<String, Object>> list = server.selectListForExcelExport(queryParams, page); list != null && list.size() > 0; list = server.selectListForExcelExport(queryParams, var6++)) {
            if (rowNum == 0) {
                //设置表头
                int n = 0;//列数
                Map<String, Object> map = list.get(0);
                SXSSFRow first = sheet.createRow(0);//创建sheet的第一行
                for (String key : map.keySet()) {
                    //创建单元格并赋值
                    first.createCell(n).setCellValue(key);
                    n++;
                }
                rowNum++;
            }
            for (Map<String, Object> data : list) {
                SXSSFRow row = sheet.createRow(rowNum);//创建sheet的第rownum+1行
                int n = 0;//列数
                for (String key : data.keySet()) {
                    row.createCell(n).setCellValue(data.get(key) != null ? data.get(key).toString() : "");
                    n++;
                }
                rowNum++;
            }
        }
        return workbook;
    }

源码地址

POI大数据量导出

<think>好的,我现在要解决用户关于EasyPoi中嵌套ExcelExportEntity与List<Map>如何映射的问题。用户之前已经询问过示例,现在可能希望更深入理解映射机制。首先,我需要回顾之前的回答,确保这次内容不重复但更详细。 用户可能在实际使用中遇到了问题,比如数据无法正确导出,或者表头结构不对。他们需要明确如何配置ExcelExportEntity的层级关系,以及Map中的键如何对应到这些实体。我需要分步骤解释,确保逻辑清晰。 首先,得解释ExcelExportEntity如何构建嵌套表头,特别是groupName的作用。用户可能对如何设置父子关系不太清楚,所以需要强调父实体和子实体如何通过groupName关联。例如,父级设置groupName,子级使用相同的groupName来绑定。 接下来,数据部分的映射是关键。用户使用List<Map>时,必须按照groupName.fieldName的格式设置键。这里需要举例说明,比如父级group是"base",子字段是"name",那么Map的键应该是"base.name"。这点容易出错,需要重点强调。 可能用户对动态生成表头感兴趣,或者如何处理多层嵌套。我需要提到动态构建的方法,比如循环创建父级和子级实体,并确保顺序正确。同时,提醒注意事项,如数据键的严格匹配、列顺序的重要性,以及样式和性能优化。 还需要检查是否有遗漏的常见问题,例如合并单元格的设置,或者导出时的类型选择。用户可能在使用过程中遇到性能问题,特别是大数据量时,所以需要提到SXSSFWorkbook的启用方法。 最后,总结关键点,帮助用户巩固理解。确保回答结构清晰,步骤明确,让用户能够一步步跟着操作,避免混乱。可能的话,提供一个简单的示例,展示从实体创建到数据填充的完整流程,这样用户可以直接参考使用。</think>在 EasyPoi 中实现**嵌套表头**与 `List<Map>` 数据的映射,核心是通过 `ExcelExportEntity` 的**层级化配置**与 `Map` 的**键名规范**实现。以下是具体映射逻辑与步骤详解: --- ### 一、映射关系示意图 ``` ExcelExportEntity 层级结构 Map数据结构 ----------------------------- ---------------------- | 父级列: 学生信息 (group="stu") | → | stu.name=张三 | |-----------------------------| | stu.age=20 | | 子列: 姓名 (group="stu") | | stu.score.math=85 | | 子列: 年龄 (group="stu") | |---------------------| | 子列: 数学成绩 (group="score")| ------------------------------- ``` --- ### 、实现步骤分解 #### 1. 定义表头层级结构 ```java List<ExcelExportEntity> columns = new ArrayList<>(); // 父级列配置(合并单元格) ExcelExportEntity parentCol = new ExcelExportEntity("学生信息", "stu"); parentCol.setGroupName("stu"); // 🟢 关键标识 parentCol.setMergeVertical(true); // 纵向合并 parentCol.setExportListField(true); // 标记为父级容器 // 子级列配置 ExcelExportEntity nameCol = new ExcelExportEntity("姓名", "name"); nameCol.setGroupName("stu"); // 🟢 必须与父级group相同 ExcelExportEntity ageCol = new ExcelExportEntity("年龄", "age"); ageCol.setGroupName("stu"); // 另一个父级列 ExcelExportEntity scoreParent = new ExcelExportEntity("成绩", "score"); scoreParent.setGroupName("score"); scoreParent.setMergeVertical(true); // 成绩子列 ExcelExportEntity mathCol = new ExcelExportEntity("数学", "math"); mathCol.setGroupName("score"); // 添加顺序:先父后子 columns.add(parentCol); columns.add(nameCol); columns.add(ageCol); columns.add(scoreParent); columns.add(mathCol); ``` #### 2. 数据映射规则 ```java List<Map<String, Object>> data = new ArrayList<>(); Map<String, Object> row = new HashMap<>(); // 规则:groupName + "." + 字段名 row.put("stu.name", "李雷"); row.put("stu.age", 22); row.put("score.math", 95); data.add(row); ``` #### 3. 导出执行 ```java ExportParams params = new ExportParams(); params.setStyle(ExcelExportStylerImpl.class); // 样式统一 Workbook workbook = ExcelExportUtil.exportExcel(params, columns, data); // 输出到文件或响应流... ``` --- ### 三、特殊场景处理 #### 1. 多级嵌套(三级表头) ```java // 三级结构:学院 → 班级 → 学生 ExcelExportEntity college = new ExcelExportEntity("学院", "college"); college.setGroupName("college_group"); ExcelExportEntity className = new ExcelExportEntity("班级", "class"); className.setGroupName("class_group"); className.setParentGroup("college_group"); // 🟢 设置父级关联 ExcelExportEntity studentName = new ExcelExportEntity("姓名", "name"); studentName.setGroupName("student_group"); studentName.setParentGroup("class_group"); ``` 数据映射: ```java row.put("college_group.class_group.student_group.name", "王芳"); ``` #### 2. 动态列处理 ```java // 根据数据库配置动态生成列 List<FieldConfig> dbColumns = getColumnsFromDB(); for (FieldConfig config : dbColumns) { if (config.isParent()) { ExcelExportEntity parent = new ExcelExportEntity(config.getTitle(), config.getGroup()); parent.setGroupName(config.getGroup()); columns.add(parent); } else { ExcelExportEntity child = new ExcelExportEntity(config.getTitle(), config.getField()); child.setGroupName(config.getParentGroup()); // 绑定父级group columns.add(child); } } ``` --- ### 四、常见问题排查表 | 问题现象 | 原因分析 | 解决方案 | |---------------------------|----------------------------|-----------------------------------| | 数据列显示为`null` | Map键名与groupName不匹配 | 检查键名是否符合`group.field`格式 | | 父级标题不合并单元格 | 未设置`setMergeVertical` | 父级列开启`setMergeVertical(true)`| | 列顺序错乱 | 添加顺序不符合父→子层级 | 确保先添加父级列再子级列 | | 大数据量导出内存溢出 | 未启用流式导出 | 设置`exportParams.setSxssfWorkbook(true)` | --- ### 五、最佳实践建议 1. **封装工具类**:将列配置与数据转换逻辑封装成`ExcelExporter`工具类 2. **数据预处理**:使用`MapStruct`或自定义转换器将DTO转换为符合规则的Map结构 3. **性能优化**:超过10万行数据时采用分页查询+分批写入模式 ```java ExcelExportServer server = new ExcelExportServer(); // 分页写入示例 for (int i = 0; i < totalPage; i++) { List<Map<String, Object>> pageData = getPageData(i); server.appendData(pageData); // 增量追加数据 } ``` 通过严格遵循 `groupName` 的绑定规则和 Map 键的命名规范,即可实现复杂嵌套表头与灵活数据结构的精确映射。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值