一般情况:

Excel导出一般都是一行一行的记录输出

这是Controller代码:

标题行的设置:

标题行会设置获取的结果集的字段名,数据会自动根据设置的名称匹配装填

特殊的需求:

如页面的效果,附加的三个字段存在多个记录,需要合并和之前的主记录拼接处理:

右侧的数据是根据这个接口提供的:

页面有用循环回调来实现

但是导出功能,开发干脆就没写这部分了???

所以BA要我把这个部分给弄出来

合并原理:

关于POI框架和用友提供的封装方法都没有这样关于记录行合并的操作:

现有的方式都是对标题行进行合并设置的

这是一段原框架中的Main方法Demo案例:

    public static void main(String[] args) {
NormalExcelExport excel = new NormalExcelExport();
SXSSFWorkbook wb = excel.createExcelWork();
List<List<ExcelCol>> colColList = new LinkedList();
List<ExcelCol> exColList = new LinkedList();
exColList.add(new ExcelCol("TEST1", "测试1", 2, 1));
exColList.add(new ExcelCol("TEST2", "测试2", 2, 1));
exColList.add(new ExcelCol("TEST3", "测试3", 1, 3));
colColList.add(exColList);
List<ExcelCol> exColList1 = new LinkedList();
exColList1.add(new ExcelCol("TEST1", "测试1"));
exColList1.add(new ExcelCol("TEST2", "测试2"));
exColList1.add(new ExcelCol("TEST3", "测试3"));
colColList.add(exColList1);
List<Map> datas = new LinkedList();
Map map1 = new HashMap();
map1.put("TEST1", "1");
map1.put("TEST2", "2");
map1.put("TEST3", "3");
datas.add(map1);
String[] fieldNames = new String[]{"TEST1", "TEST2", "TEST3"};
excel.addSheetMerge(wb, colColList, fieldNames, datas, "第1页"); try {
excel.saveLocal(wb, "F:/", "test1.xlsx");
} catch (Exception var10) {
var10.printStackTrace();
} }

文件效果:

每一次添加之后该方法会将迭代器拨动至下一个基本单元行

 colColList.add(exColList);

方法执行之后,迭代器换行至这个位置:

这时就是程序理解的第二行:

添加完第二行记录之后,迭代器切换至第三行:

所以到这里我们就明白合并操作的原理了

这是我写的一个简单的案例:

    private static void demo2() {
NormalExcelExport excel = new NormalExcelExport();
SXSSFWorkbook wb = excel.createExcelWork(); // 总表格
List<List<ExcelCol>> colColList = new LinkedList(); // 标题行
List<ExcelCol> exColList = new LinkedList();
exColList.add(new ExcelCol("TEST1", "标题1"));
exColList.add(new ExcelCol("TEST2", "标题2"));
exColList.add(new ExcelCol("TEST3", "标题3"));
// 要合并的行标题
exColList.add(new ExcelCol("TEST4", "标题4"));
exColList.add(new ExcelCol("TEST5", "标题5"));
exColList.add(new ExcelCol("TEST6", "标题6")); colColList.add(exColList); // 单次合并实现
List<ExcelCol> leftSideRow = new LinkedList(); // 左侧单元格 实现行合并
leftSideRow.add(new ExcelCol("TEST1", "AAA", 2, 1));
leftSideRow.add(new ExcelCol("TEST2", "BBB", 2, 1));
leftSideRow.add(new ExcelCol("TEST3", "CCC", 2, 1));
leftSideRow.add(new ExcelCol("TEST4", "41"));
leftSideRow.add(new ExcelCol("TEST5", "42"));
leftSideRow.add(new ExcelCol("TEST6", "43"));
colColList.add(leftSideRow);
leftSideRow = new LinkedList<>();
leftSideRow.add(new ExcelCol("TEST4", "51"));
leftSideRow.add(new ExcelCol("TEST5", "52"));
leftSideRow.add(new ExcelCol("TEST6", "53"));
colColList.add(leftSideRow); List<ExcelCol> smapleRow = new LinkedList();
smapleRow.add(new ExcelCol("TEST1", "AAA3", 3, 1));
smapleRow.add(new ExcelCol("TEST2", "AAA4", 3, 1));
smapleRow.add(new ExcelCol("TEST3", "AAA5", 3, 1));
smapleRow.add(new ExcelCol("TEST4", "AAA6"));
smapleRow.add(new ExcelCol("TEST5", "AAA7"));
smapleRow.add(new ExcelCol("TEST6", "AAA8")); colColList.add(smapleRow); // 换行时注意 前3个合并了3行,下一行会是 AA5开始
smapleRow = new LinkedList<>();
smapleRow.add(new ExcelCol("TEST4", "AAA26"));
smapleRow.add(new ExcelCol("TEST5", "AAA27"));
smapleRow.add(new ExcelCol("TEST6", "AAA28"));
colColList.add(smapleRow);
smapleRow = new LinkedList<>();
smapleRow.add(new ExcelCol("TEST4", "AAA26"));
smapleRow.add(new ExcelCol("TEST5", "AAA27"));
smapleRow.add(new ExcelCol("TEST6", "AAA28"));
colColList.add(smapleRow); excel.addSheetMerge(wb, colColList, null, null, "第1页"); try {
excel.saveLocal(wb, "D:/", "test1.xlsx");
} catch (Exception var10) {
var10.printStackTrace();
}
}

回到业务需求:

    /**
*
* @param queryParam
* @return
* @throws Exception
*/
@RequestMapping(value = "/exportData2", method = RequestMethod.GET)
@ResponseBody
public Map<String, Object> SSPInvoiceSumReportExport2(@RequestParam Map<String, String> queryParam)
throws Exception {
ElemBean condition = new ElemBean(queryParam);
List<List<ExcelCol>> colColList = new LinkedList();
// 设置标题行
List<ExcelCol> titleColList = new LinkedList<ExcelCol>();
titleColList.add(new ExcelCol("asc_code","维修站代码"));
titleColList.add(new ExcelCol("SAP_CODE","SAP码"));
titleColList.add(new ExcelCol("asc_name","维修站名称"));
titleColList.add(new ExcelCol("invoice_no","发票号"));
titleColList.add(new ExcelCol("no_tax_amount","不含税金额"));
titleColList.add(new ExcelCol("invoice_amount","含税金额"));
titleColList.add(new ExcelCol("tax_amount","税费"));
titleColList.add(new ExcelCol("audit_status","状态", ExcelDataType.DICT));
titleColList.add(new ExcelCol("invoice_date","发票日期",ExcelDataType.DATEYYYYMMDD));
titleColList.add(new ExcelCol("created_at","提报日期",ExcelDataType.DATEYYYYMMDD));
titleColList.add(new ExcelCol("sap_code1","冲收入代码"));
titleColList.add(new ExcelCol("product_price","合同金额"));
titleColList.add(new ExcelCol("no_tax_income_amount","收入金额(不含税)"));
colColList.add(titleColList); // 需要getInvoiceQuery的查询条件 设置 limit 9999999
// PageInfoDto pageInfoDto = service.getInvoiceQuery(new ElemBean(queryParam)); List<Object> params = new LinkedList<Object>();
String sql = service.getSspInvoiceSumReportExpSql(condition, params);
List<Map> pageInfoDto = DcsDaoUtil.findAll(sql, params); // 左侧的主结果集 Map<String,String> distMap = new LinkedHashMap<>();
distMap.put("59701001","已提交");
distMap.put("59701002","已审核");
distMap.put("59701003","已驳回");
distMap.put("59701004","已核销"); for (int i = 0; i < pageInfoDto.size(); i++) {
// 当前行
Map currentRow =
pageInfoDto.get(i); String invoice_no = StringUtils.isNullOrEmpty(currentRow.get("invoice_no")) ? "" : currentRow.get("invoice_no").toString(); // 发票单号为空的情况
List<Map> invoiceDetail = new LinkedList<>();
// 右侧的
if(!"".equals(invoice_no) && invoice_no != null){
invoiceDetail = service.getInvoiceDetail(invoice_no, null); // 右侧结果集
}
titleColList = new LinkedList<>();
titleColList.add(new ExcelCol("asc_code", StringUtils.isNullOrEmpty(currentRow.get("asc_code")) ? "" : currentRow.get("asc_code").toString(),invoiceDetail.size() == 0 ? 1 : invoiceDetail.size() , 1));
titleColList.add(new ExcelCol("SAP_CODE", StringUtils.isNullOrEmpty(currentRow.get("SAP_CODE")) ? "" : currentRow.get("SAP_CODE").toString(),invoiceDetail.size() == 0 ? 1 : invoiceDetail.size() , 1));
titleColList.add(new ExcelCol("asc_name", StringUtils.isNullOrEmpty(currentRow.get("asc_name")) ? "" : currentRow.get("asc_name").toString(),invoiceDetail.size() == 0 ? 1 : invoiceDetail.size() , 1));
titleColList.add(new ExcelCol("invoice_no", StringUtils.isNullOrEmpty(currentRow.get("invoice_no")) ? "" : currentRow.get("invoice_no").toString(),invoiceDetail.size() == 0 ? 1 : invoiceDetail.size() , 1));
titleColList.add(new ExcelCol("no_tax_amount", StringUtils.isNullOrEmpty(currentRow.get("no_tax_amount")) ? "" : currentRow.get("no_tax_amount").toString(),invoiceDetail.size() == 0 ? 1 : invoiceDetail.size() , 1));
titleColList.add(new ExcelCol("invoice_amount", StringUtils.isNullOrEmpty(currentRow.get("invoice_amount")) ? "" : currentRow.get("invoice_amount").toString(),invoiceDetail.size() == 0 ? 1 : invoiceDetail.size() , 1));
titleColList.add(new ExcelCol("tax_amount", StringUtils.isNullOrEmpty(currentRow.get("tax_amount")) ? "" : currentRow.get("tax_amount").toString(),invoiceDetail.size() == 0 ? 1 : invoiceDetail.size() , 1));
titleColList.add(new ExcelCol("audit_status", StringUtils.isNullOrEmpty(currentRow.get("audit_status")) ? "" : distMap.get( currentRow.get("audit_status").toString()),invoiceDetail.size() == 0 ? 1 : invoiceDetail.size() , 1));
titleColList.add(new ExcelCol("invoice_date", StringUtils.isNullOrEmpty(currentRow.get("invoice_date")) ? "" : currentRow.get("invoice_date").toString().substring(0,10),invoiceDetail.size() == 0 ? 1 : invoiceDetail.size() , 1));
titleColList.add(new ExcelCol("created_at", StringUtils.isNullOrEmpty(currentRow.get("created_at")) ? "" : currentRow.get("created_at").toString().substring(0,10),invoiceDetail.size() == 0 ? 1 : invoiceDetail.size() , 1));
for(int j = 0 ; j < invoiceDetail.size() ; j ++){
if(j == 0){ // 第一行的的时候就需要和前面的内容 并列为一行
titleColList.add(new ExcelCol("sap_code1",StringUtils.isNullOrEmpty(invoiceDetail.get(j).get("sap_code1")) ? "" : invoiceDetail.get(j).get("sap_code1").toString()));
titleColList.add(new ExcelCol("product_price",StringUtils.isNullOrEmpty(invoiceDetail.get(j).get("product_price")) ? "" : invoiceDetail.get(j).get("product_price").toString()));
titleColList.add(new ExcelCol("no_tax_income_amount",StringUtils.isNullOrEmpty(invoiceDetail.get(j).get("no_tax_income_amount")) ? "" : invoiceDetail.get(j).get("no_tax_income_amount").toString()));
colColList.add(titleColList);
}else{ // 后面下推的行记录就是新的一行了
titleColList = new LinkedList<>();
titleColList.add(new ExcelCol("sap_code1",StringUtils.isNullOrEmpty(invoiceDetail.get(j).get("sap_code1")) ? "" : invoiceDetail.get(j).get("sap_code1").toString()));
titleColList.add(new ExcelCol("product_price",StringUtils.isNullOrEmpty(invoiceDetail.get(j).get("product_price")) ? "" : invoiceDetail.get(j).get("product_price").toString()));
titleColList.add(new ExcelCol("no_tax_income_amount",StringUtils.isNullOrEmpty(invoiceDetail.get(j).get("no_tax_income_amount")) ? "" : invoiceDetail.get(j).get("no_tax_income_amount").toString()));
colColList.add(titleColList);
} } if(invoiceDetail.size() == 0){ // 还存在右边结果集查不到结果的情况,这一行也要保留为空记录
titleColList.add(new ExcelCol("sap_code1",""));
titleColList.add(new ExcelCol("product_price",""));
titleColList.add(new ExcelCol("no_tax_income_amount",""));
colColList.add(titleColList);
}
} // for local tested , write by dzz 2021年5月20日18:28:41
// NormalExcelExport excel = new NormalExcelExport();
//
// SXSSFWorkbook wb = excel.createExcelWork();
//
// excel.addSheetMerge(wb, colColList, null, null, "第1页");
// excel.saveLocal(wb, "D:/", "test1.xlsx"); Map<String, Object> maps = jmcExc.addSheetMerge(colColList, null, " select * from (select 1 + 1 ) a where 1 = 2 ", null, "SSP发票汇总报表.xlsx", "SSP发票汇总报表", null);
return maps;
}

最后的调用方法:

 Map<String, Object> maps = jmcExc.addSheetMerge(colColList, null, " select * from (select 1 + 1 ) a where 1 = 2 ", null, "SSP发票汇总报表.xlsx", "SSP发票汇总报表", null);

SQL参数时方法中有一个数据装填操作,如果SQL没有记录就不会装数据执行:

无记录的SQL:

" select * from (select 1 + 1 ) a where 1 = 2 "

要求的字段名为空,SQL参数空

最后直接把这个【标题结果集】丢进去,就实现了页面的那种效果

【Java】POI Excel导出 动态行合并的更多相关文章

  1. java POI excel 导出复合样式(一个单元格两个字体)

    前言:java poi 导出 excel 时,需要设置一个单元格有多个字体样式,有点类似于富文本. 想要达到的效果(一个单元格里): 我使用的 poi 版本是 <dependency> & ...

  2. Java POI导入导出Excel

    1.异常java.lang.NoClassDefFoundError: org/apache/poi/UnsupportedFileFormatException 解决方法: 使用的poi的相关jar ...

  3. Java POI Excel 导入导出

    这个东西很容易懂,不是特别难,难就难在一些复杂的计算和Excel格式的调整上. 近期写了一个小列子,放上来便于以后使用. POI.jar下载地址:http://mirror.bit.edu.cn/ap ...

  4. poi excel超出65536行数限制自动扩展Invalid row number (65536) outside allow

    1.xls一个sheet只能装65536行,多余则报错 poi包导出或写入excel超出65536报错: java.lang.IllegalArgumentException: Invalid row ...

  5. JAVA实现Excel导出数据(以写好的Excel模版导出)

    工作中经常会有将后台数据以Excel导出的功能. 简单的方法有将response的contentType设置为application/vnd.ms-excel: 或在JSP页面直接设置成: <% ...

  6. java中Excel导出

    转载:https://www.cnblogs.com/gudongcheng/p/8268909.html,稍加修改了 https://www.cnblogs.com/hanfeihanfei/p/7 ...

  7. java-excel导出

    java excel导出分为两种2003年的格式和2007年的格式. 2003年的xls一个sheet限制65536. 2007年的xlsx限制为1048576. jxl导入2003 gradle j ...

  8. poi excel导出 xssf 带下拉框

    需求:导出之后带有二级级联的下拉框.(类似于省市). 最初的思路是怀疑是不是数组内串太多了,导出之后的excel有36行,调试的误区在于刚开始认为对行数有限制,后自己写了一个测试类,才发现不是行数,而 ...

  9. java, poi, excel

    工作需要用java操作Excel,现在网上搜索了一下,决定选取POI包来操作.pom内容如下: <dependency> <groupId>org.apache.poi< ...

  10. JAVA POI XSSFWorkbook导出扩展名为xlsx的Excel,附带weblogic 项目导出Excel文件错误的解决方案

    现在很多系统都有导出excel的功能,总结一下自己之前写的,希望能帮到其他人,这里我用的是XSSFWorkbook,我们项目在winsang 用的Tomcat,LInux上用的weblogic服务器, ...

随机推荐

  1. SELinux 安全模型——MLS

    首发公号:Rand_cs SELinux 安全模型--MLS BLP 模型:于1973年被提出,是一种模拟军事安全策略的计算机访问控制模型,它是最早也是最常用的一种多级访问控制模型,主要用于保证系统信 ...

  2. ABC332

    D 我们可以把矩阵 \(\text{A}\) 看成 \({p,q}\). \(p\) 指现在一行最开始在哪里,\(q\) 指现在这一列最开始在哪里. 于是我们枚举 \(p\) 和 \(q\) 所有可能 ...

  3. OpenSearch 与 Elasticsearch:哪个开源搜索引擎适合您?

    当谈论到搜索引擎产品时,Elasticsearch 和 OpenSearch 是两个备受关注的选择.它们都以其出色的功能和灵活性而闻名,但在一些方面存在一些差异.在本文中,我们将从功能和延展性.工具与 ...

  4. 搜狗五笔 date 自定义时间格式

    搜狗五笔 date 自定义时间格式 搜狗五笔 date 默认打出的是"2023-6-15",如果需要"2023-06-15",则需要在自定义短语中设置 属性设置 ...

  5. 使用 OpenTelemetry 构建可观测性 01 - 介绍

    毫无疑问,在过去几年里,你可能已经多次听到过可观测性这个词.对于很多人来说,很难理解这个词的真正含义.对许多人来说,他们错误地将其等同于"监控".虽然可观测性的根本定义以及它所包含 ...

  6. session 和 cookie 有什么区别?

    a.存储位置不同:session 存储在服务器端:cookie 存储在浏览器端. b.安全性不同:cookie 安全性一般,在浏览器存储,可以被伪造和修改. c.容量和个数限制:cookie 有容量限 ...

  7. fs.1.10 ON rockeylinux8 dockerfile模式

    概述 freeswitch是一款简单好用的VOIP开源软交换平台. rockeylinux8 docker上编译安装fs.1.10的流程记录,本文使用dockerfile模式. 环境 docker e ...

  8. Xcode编译错误看不到错误详情

    问题描述 Xcode提示错误后想详细看报错信息无论如何双击都不见弹出错误详情. 解决办法 不知道是bug还是苹果婊又自作主张改变用户习惯了,需要点击navigator最右边的一个图标,在里面找到相应的 ...

  9. Ubuntu 使用 SVN 管理 项目

    背景 公司的项目需要在 Linux 环境进行开发,而都是使用 SVN 进行管理的.习惯了 SVN-GUI 的我,需要学习 SVN 的命令行. 准备 安装 SVN sudo apt-get apt-ge ...

  10. NXP i.MX 8M Mini工业核心板硬件说明书(四核ARM Cortex-A53 + 单核ARM Cortex-M4,主频1.6GHz)

    1          硬件资源 创龙科技SOM-TLIMX8是一款基于NXP i.MX 8M Mini的四核ARM Cortex-A53 + 单核ARM Cortex-M4异构多核处理器设计的高端工业 ...