【Java】POI Excel导出 动态行合并
一般情况:
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导出 动态行合并的更多相关文章
- java POI excel 导出复合样式(一个单元格两个字体)
前言:java poi 导出 excel 时,需要设置一个单元格有多个字体样式,有点类似于富文本. 想要达到的效果(一个单元格里): 我使用的 poi 版本是 <dependency> & ...
- Java POI导入导出Excel
1.异常java.lang.NoClassDefFoundError: org/apache/poi/UnsupportedFileFormatException 解决方法: 使用的poi的相关jar ...
- Java POI Excel 导入导出
这个东西很容易懂,不是特别难,难就难在一些复杂的计算和Excel格式的调整上. 近期写了一个小列子,放上来便于以后使用. POI.jar下载地址:http://mirror.bit.edu.cn/ap ...
- poi excel超出65536行数限制自动扩展Invalid row number (65536) outside allow
1.xls一个sheet只能装65536行,多余则报错 poi包导出或写入excel超出65536报错: java.lang.IllegalArgumentException: Invalid row ...
- JAVA实现Excel导出数据(以写好的Excel模版导出)
工作中经常会有将后台数据以Excel导出的功能. 简单的方法有将response的contentType设置为application/vnd.ms-excel: 或在JSP页面直接设置成: <% ...
- java中Excel导出
转载:https://www.cnblogs.com/gudongcheng/p/8268909.html,稍加修改了 https://www.cnblogs.com/hanfeihanfei/p/7 ...
- java-excel导出
java excel导出分为两种2003年的格式和2007年的格式. 2003年的xls一个sheet限制65536. 2007年的xlsx限制为1048576. jxl导入2003 gradle j ...
- poi excel导出 xssf 带下拉框
需求:导出之后带有二级级联的下拉框.(类似于省市). 最初的思路是怀疑是不是数组内串太多了,导出之后的excel有36行,调试的误区在于刚开始认为对行数有限制,后自己写了一个测试类,才发现不是行数,而 ...
- java, poi, excel
工作需要用java操作Excel,现在网上搜索了一下,决定选取POI包来操作.pom内容如下: <dependency> <groupId>org.apache.poi< ...
- JAVA POI XSSFWorkbook导出扩展名为xlsx的Excel,附带weblogic 项目导出Excel文件错误的解决方案
现在很多系统都有导出excel的功能,总结一下自己之前写的,希望能帮到其他人,这里我用的是XSSFWorkbook,我们项目在winsang 用的Tomcat,LInux上用的weblogic服务器, ...
随机推荐
- 绘图与可视化--matplotlib API入门
matplotlib API函数都位于matplotlib.pyplot模块中. 本节代码中引入的约定为:import matplotlib.pyplot as plt 另外,numpy库也会用到,约 ...
- WIn32 C++ 消息处理函数 问题
这个消息处理这个 Winproc 这个 接收到网络信息 在自己的函数用完后可以选择向系统路由传递这个网络消息接收到的数据原型 你处理完,系统也处理,不想让系统处理可以不将接受到的那几个变量啊数据啊,就 ...
- 像 Google SRE 一样 OnCall
在 Google SRE 的著作<Google运维解密>(原作名:Site Reliability Engineering: How Google Runs Production Syst ...
- (十)selenium实现微博高级搜索信息爬取
1.selenium模拟登陆 2.定位进入高级搜索页面 3.对高级搜索进行定位,设置. 4.代码实现 import time from selenium import webdriver from l ...
- H5弹窗底层滑动
H5弹窗底层滑动 背景 产品提出H5 弹出窗滑动时,底层页面也会跟随滑动,需要调整禁止底层滑动,增加用户体验. 问题产生原因 ios 滑动时有回弹效果 顶层元素滑动默认行为 解决办法 阻止元素的默认( ...
- html 适配iphone X 底部导航
html 适配iphone X 底部导航 适配手机底部有横线的手机,以免被横线遮挡 padding-bottom: constant(safe-area-inset-bottom); /* 兼容 iO ...
- VMware 虚拟机下载
VMware破解版下载地址: https://www.aliyundrive.com/s/CUr4eaMgxGa 提取码:e0c8 激活码: ZF3R0-FHED2-M80TY-8QYGC-NPKYF ...
- sprak应用执行过程
- .NET CORE 部署提示 An error occurred while starting the application.
错误提示: 解决方法 检查一下nuget引用包 是否更新了版本,如果升级或者降级了版本,需要将新的dll文件更新一下
- Python数据分析方法与技巧
背景介绍 数据分析是数据科学领域的核心技能之一,它涉及到数据的收集.清洗.处理.分析和可视化. 数据分析是指通过收集.清洗.处理.分析和可视化数据来发现隐藏的模式.趋势和关系的过程. 数据分析是数据科 ...