一般情况:

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. minos 2.1 中断虚拟化——ARMv8 异常处理

    首发公号:Rand_cs 越往后,交叉的越多,大多都绕不开 ARMv8 的异常处理,所以必须得先了解了解 ARMv8 的异常处理流程 先说一下术语,从手册中的用词来看,在 x86 平台,一般将异常和中 ...

  2. INFINI Labs 产品更新 | Console 1.24.0 操作日志审计功能发布

    INFINI Labs 产品又更新啦~,包括 Console,Gateway 1.24.0.本次各产品更新了很多亮点功能,如 Console 增加操作日志审计功能,优化数据探索字段统计,修复 Gate ...

  3. 机器学习决策树ID3算法,python实现代码

    机器学习决策树ID3算法,python实现代码 看到techflow介绍ID3算法,中间有代码示例.代码尝试执行力下,发现有错误. https://www.cnblogs.com/techflow/p ...

  4. 如何解决系统报错:nf_conntrack: table full, dropping packets

    问题 在系统日志中(/var/log/messages),有时会看到大面积的下面的报错: nf_conntrack: table full, dropping packet 这说明系统接到了大量的连接 ...

  5. python 判断token是否有效,若失效,重新发起token请求

    场景: 1.对一个接口,进行接口自动化测试,查找的是有权限操作的用例,传入到获取token接口,生成token,判断当前是否有token,如果存在token,则无需再次发起token接口: 存在的问题 ...

  6. 数据标注工具 doccano | 命名实体识别(Named Entity Recognition,简称NER)

    目录 安装 数据准备 创建项目 创建抽取式任务 上传 定义标签 构建抽取式任务标签 任务标注 命名实体识别 导出数据 查看数据 命名实体识别(Named Entity Recognition,简称NE ...

  7. 你了解Vim的增删改查吗 ?

    增: 在Vim的Normal模式中输入A/I/O,a/i/o字符进行对应的增加操作. 删 在Vim的Normal模式中, 输入x 删除光标对应的一个字符(4x代表删除4个字符): 输入dd删除光标所在 ...

  8. dense并行训练1-流水线并行

    并行训练-流水线 简述 并行训练主要有三种策略: 数据并行训练加速比最高,但要求每个设备上都备份一份模型,显存占用比较高,但缺点是通信量大. 张量并行,通信量比较高,适合在机器内做模型并行. 流水线并 ...

  9. VS Code Go开发环境配置

    1.安装Go 下载网址:https://go.dev/doc/install 根据自己的操作系统来进行安装,官网针对Windows.Linux.macOS都有对应教程.安装完成后打开终端,输入go v ...

  10. Apline部署K3s的Agent

    之前我们在Ubuntu上部署了K3s的Server节点(传送门),这次我们加入两台K3s的Agent节点搭建一个K3s的3节点工作环境. 需要准备好网络环境,确保三台VM之间是可以ping通的,设置好 ...