easy-poi 一对多导出

1. 需求:

某一列上下两行单元格A,B值一样且这两个单元格, 前面所有列对应单元格值一样的话,

就对A,B 两个单元格进行纵向合并单元格

1. 核心思路:

先对数据集的国家,省份,城市...... id 身份证进行排序

国家一列,值相同就合并单元格(直接调用:2 是指的下标 是2 开始;0 是0列

PoiMergeCellUtil.mergeCells(sheet,2,0);

省份一列:

两行数据,前一列的值相同(国家列相同),且当前列对应值也相同就合并单元格

城市一列:

两行数据,第一列+第二列值相同(国家省份值相同),且当前列对应值也相同就合并单元格

其他类似:

POM文件如下:

  <!-- EasyPoi 核心库 --><dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-base</artifactId><version>4.2.0</version></dependency><!-- EasyPoi Web 支持 --><dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-web</artifactId><version>4.2.0</version></dependency><!-- 如果需要使用注解 --><dependency><groupId>cn.afterturn</groupId><artifactId>easypoi-annotation</artifactId><version>4.2.0</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>4.1.2</version></dependency>

代码如下:

实体类:

package com.example.demo.entity;import cn.afterturn.easypoi.excel.annotation.Excel;
import lombok.Data;
import org.springframework.util.ObjectUtils;import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;/*** @author guoyiguang* @description $* @date 2025/4/5$*/
@Data
public class Boy {@Excel(name = "国家",orderNum = "1")private String country;@Excel(name = "省份",orderNum = "2")private String province;@Excel(name = "城市",orderNum = "3")private String city;@Excel(name = "县",orderNum = "4")private String county;@Excel(name = "城镇",orderNum = "5")private String town; // 镇@Excel(name = "村",orderNum = "6")private String village; // 村@Excel(name = "街道",orderNum = "7")private String street;@Excel(name = "性别",orderNum = "8")private String sex;@Excel(name = "名称",orderNum = "9")private String name;@Excel(name = "出生年份",orderNum = "10")private String birthYear;@Excel(name = "出生月份份",orderNum = "11")private String birthMonth; //@Excel(name = "ID身份证",orderNum = "12")private String idCard; // 身份证标识}

模拟从数据库获取业务数据:

    public List<Boy> getBoysList(){List<Boy> boyList = new ArrayList<>();Boy boy = new Boy();boy.setCountry("中国");boy.setProvince("山西省");boy.setCity("晋中市");boy.setCounty("平遥县");boy.setTown("岳壁乡");boy.setVillage("金村");boy.setStreet("向阳街道");boy.setBirthYear("1990");boy.setBirthMonth("02");boy.setSex("男");boy.setName("张三1");boy.setIdCard("张三1");boyList.add(boy);Boy boy7 = new Boy();boy7.setCountry("中国");boy7.setProvince("山西省");boy7.setCity("晋中市");boy7.setCounty("平遥县");boy7.setTown("岳壁乡");boy7.setVillage("金村");boy7.setStreet("向阳街道");boy7.setBirthYear("1990");boy7.setBirthMonth("02");boy7.setSex("男");boy7.setName("张三1");boy7.setIdCard("张三111");boyList.add(boy7);Boy boy2 = new Boy();boy2.setCountry("中国");boy2.setProvince("山西省");boy2.setCity("晋中市");boy2.setCounty("平遥县");boy2.setTown("岳壁乡");boy2.setVillage("金村");boy2.setStreet("向阳街道-2");boy2.setBirthYear("1990");boy2.setBirthMonth("02");boy2.setSex("男");boy2.setName("张三2");boy2.setIdCard("张三2");boyList.add(boy2);Boy boy8 = new Boy();boy8.setCountry("中国");boy8.setProvince("山西省");boy8.setCity("晋中市");boy8.setCounty("平遥县");boy8.setTown("岳壁乡");boy8.setVillage("金村");boy8.setStreet("向阳街道-3");boy8.setBirthYear("1990");boy8.setBirthMonth("02");boy8.setSex("男");boy8.setName("张三3");boy8.setIdCard("张三33");boyList.add(boy8);Boy boy4 = new Boy();boy4.setCountry("中国");boy4.setProvince("陕西省");boy4.setCity("渭南市");boy4.setCounty("渭南县");boy4.setTown("渭南乡");boy4.setVillage("渭南村");boy4.setStreet("渭南向阳街道");boy4.setBirthYear("1990");boy4.setBirthMonth("02");boy4.setSex("男");boy4.setName("张三1");boy4.setIdCard("渭南张三1");boyList.add(boy4);Boy boy10 = new Boy();boy10.setCountry("中国");boy10.setProvince("陕西省");boy10.setCity("渭南市");boy10.setCounty("渭南县");boy10.setTown("渭南乡");boy10.setVillage("渭南村");boy10.setStreet("渭南向阳街道");boy10.setBirthYear("1990");boy10.setBirthMonth("02");boy10.setSex("男");boy10.setName("李四");boy10.setIdCard("渭南李四");boyList.add(boy10);Boy boy5 = new Boy();boy5.setCountry("中国");boy5.setProvince("陕西省");boy5.setCity("渭南市");boy5.setCounty("渭南县2");boy5.setTown("渭南乡2");boy5.setVillage("渭南村2");boy5.setStreet("渭南向阳街道");boy5.setBirthYear("1990");boy5.setBirthMonth("02");boy5.setSex("男");boy5.setName("张三1");boy5.setIdCard("渭南张三1");boyList.add(boy5);Boy boy9 = new Boy();boy9.setCountry("中国");boy9.setProvince("陕西省");boy9.setCity("咸阳市");boy9.setCounty("咸阳县2");boy9.setTown("咸阳乡2");boy9.setVillage("咸阳村2");boy9.setStreet("咸阳向阳街道");boy9.setBirthYear("1990");boy9.setBirthMonth("02");boy9.setSex("男");boy9.setName("张三1");boy9.setIdCard("咸阳张三1");boyList.add(boy9);Boy boy3 = new Boy();boy3.setCountry("美国");boy3.setProvince("美国省");boy3.setCity("美国市");boy3.setCounty("美国县");boy3.setTown("美国乡");boy3.setVillage("美国村");boy3.setStreet("美国街道");boy3.setBirthYear("1990");boy3.setBirthMonth("02");boy3.setSex("男");boy3.setName("美国张三2");boy3.setIdCard("美国张三2");boyList.add(boy3);Boy boy6 = new Boy();boy6.setCountry("美国");boy6.setProvince("美国省");boy6.setCity("美国市");boy6.setCounty("美国县");boy6.setTown("美国乡");boy6.setVillage("美国村-2");boy6.setStreet("美国街道");boy6.setBirthYear("1990");boy6.setBirthMonth("02");boy6.setSex("男");boy6.setName("美国张三2");boy6.setIdCard("美国张三2");boyList.add(boy6);return boyList;}

某一列两个单元格是否合并的工具方法:

    public void setMergeStartEndRow(LinkedList<Pair> list,int curRow,String preLastContent, String preCurContents,String lastContent,String curContent){if(!ObjectUtils.isEmpty(preLastContent) && !ObjectUtils.isEmpty(preCurContents) &&  preLastContent.equals(preCurContents)){if(lastContent.equals(curContent)){if(!CollectionUtils.isEmpty(list)){Pair lastPair = list.getLast();// 某一列要合并的单元格增加了一行if((int)lastPair.getValue() == curRow-1 ){Pair pair = list.removeLast();list.add(Pair.of(pair.getLeft(),curRow));}else{// 某一列这两行要合并list.add(Pair.of(curRow-1,curRow));}}else{// 某一列这两行要合并list.add(Pair.of(curRow-1,curRow));}}else{}}else{// 不相等不处理}}

测试方法:

 public void exportBoys(HttpServletResponse response) throws IOException {List<Boy> boysList = getBoysList();//  0 行,0列// TOTO 根据字段排序boysList.sort(Comparator.comparing(Boy::getCountry).thenComparing(Boy::getProvince).thenComparing(Boy::getCity).thenComparing(Boy::getCounty).thenComparing(Boy::getTown));// 第二列到第十列合并依据:两行数据前一列值相同(更准确的说法:某两行某一列,之前所有列对应的两行数据都相同)且两行数据当前列的value一样// eg//row1: 中国  北京市  海淀区  西二旗(当前列)//row2: 中国  北京市  海淀区  西二旗(当前列)// 核心代码:构建 sheet.addMergedRegion(new CellRangeAddress(startRow, endRow, column, column)); 的 startRow 和 endRowLinkedList<Pair> secondList = new LinkedList<>();LinkedList<Pair> list2 = new LinkedList<>();LinkedList<Pair> list3 = new LinkedList<>();LinkedList<Pair> list4 = new LinkedList<>();LinkedList<Pair> list5 = new LinkedList<>();LinkedList<Pair> list6 = new LinkedList<>();LinkedList<Pair> list7 = new LinkedList<>();LinkedList<Pair> list8 = new LinkedList<>();LinkedList<Pair> list9 = new LinkedList<>();LinkedList<Pair> list10 = new LinkedList<>();for(int row = 0;row <= boysList.size()-1;row++){if(row ==0 ){continue;}Boy curBoy = boysList.get(row);Boy lastBoy = boysList.get(row-1); // 上一行数据// 省份合并(要看前面国家和当前省份是否一样+当前行值和上一行值一样)setMergeStartEndRow(secondList,row,lastBoy.getCountry(),curBoy.getCountry(),lastBoy.getProvince(),curBoy.getProvince());//          城市合并(要看前面国家和前面省份是否一样(前面所有字段值都一样才合并)+当前行值和上一行值一样)setMergeStartEndRow(list2,row,lastBoy.getProvince(),curBoy.getProvince(),lastBoy.getCity(),curBoy.getCity());// 县合并setMergeStartEndRow(list3,row,lastBoy.getCity(),curBoy.getCity(),lastBoy.getCounty(),curBoy.getCounty());// 城镇合并setMergeStartEndRow(list4,row,lastBoy.getCounty(),curBoy.getCounty(),lastBoy.getTown(),curBoy.getTown());// 村合并()setMergeStartEndRow(list5,row,lastBoy.getTown(),curBoy.getTown(),lastBoy.getVillage(),curBoy.getVillage());// 街道合并setMergeStartEndRow(list6,row,lastBoy.getVillage(),curBoy.getVillage(),lastBoy.getStreet(),curBoy.getStreet());// 性别合并(城镇+村+街道 都一样才认为横向条件满足)setMergeStartEndRow(list7,row,getPrexStr(lastBoy.getTown(),lastBoy.getVillage(),lastBoy.getStreet()),getPrexStr(curBoy.getTown(),curBoy.getVillage(),curBoy.getStreet()),lastBoy.getSex(),curBoy.getSex());// name 合并 (城镇+村+街道+性别 都一样才认为横向条件满足)setMergeStartEndRow(list8,row,getPrexStr(lastBoy.getTown(),lastBoy.getVillage(),lastBoy.getStreet(),lastBoy.getSex()),getPrexStr(curBoy.getTown(),curBoy.getVillage(),curBoy.getStreet(),curBoy.getSex()),lastBoy.getName(),curBoy.getName());//年份合并setMergeStartEndRow(list9,row,lastBoy.getName(),curBoy.getName(),lastBoy.getBirthYear(),curBoy.getBirthYear());// 月份合并 (城镇+村+街道+性别+名称+年份 都一样才认为横向条件满足)setMergeStartEndRow(list10,row,getPrexStr(lastBoy.getTown(),lastBoy.getVillage(),lastBoy.getStreet(),lastBoy.getSex(),lastBoy.getName(),lastBoy.getBirthYear()),getPrexStr(curBoy.getTown(),curBoy.getVillage(),curBoy.getStreet(),curBoy.getSex(),curBoy.getName(),curBoy.getBirthYear()),lastBoy.getBirthMonth(),curBoy.getBirthMonth());}Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams("标题", "副标题"), Boy.class, boysList);Sheet sheet = workbook.getSheet("副标题");// 第一列PoiMergeCellUtil.mergeCells(sheet,2,0);secondList.forEach(pair->{// 标题占了两行,+2sheet.addMergedRegion(new CellRangeAddress((int)pair.getLeft()+2, (int)pair.getValue()+2, 1, 1));});list2.forEach(pair->{// 标题占了两行,+2sheet.addMergedRegion(new CellRangeAddress((int)pair.getLeft()+2, (int)pair.getValue()+2, 2, 2));});list3.forEach(pair->{// 标题占了两行,+2sheet.addMergedRegion(new CellRangeAddress((int)pair.getLeft()+2, (int)pair.getValue()+2, 3, 3));});list4.forEach(pair->{// 标题占了两行,+2sheet.addMergedRegion(new CellRangeAddress((int)pair.getLeft()+2, (int)pair.getValue()+2, 4, 4));});list5.forEach(pair->{// 标题占了两行,+2sheet.addMergedRegion(new CellRangeAddress((int)pair.getLeft()+2, (int)pair.getValue()+2, 5, 5));});list6.forEach(pair->{// 标题占了两行,+2sheet.addMergedRegion(new CellRangeAddress((int)pair.getLeft()+2, (int)pair.getValue()+2, 6, 6));});list7.forEach(pair->{// 标题占了两行,+2sheet.addMergedRegion(new CellRangeAddress((int)pair.getLeft()+2, (int)pair.getValue()+2, 7, 7));});list8.forEach(pair->{// 标题占了两行,+2sheet.addMergedRegion(new CellRangeAddress((int)pair.getLeft()+2, (int)pair.getValue()+2, 8, 8));});list9.forEach(pair->{// 标题占了两行,+2sheet.addMergedRegion(new CellRangeAddress((int)pair.getLeft()+2, (int)pair.getValue()+2, 9, 9));});list10.forEach(pair->{// 标题占了两行,+2sheet.addMergedRegion(new CellRangeAddress((int)pair.getLeft()+2, (int)pair.getValue()+2, 10, 10));});response.setContentType("application/vnd.ms-excel");response.setHeader("Content-disposition", "attachment;filename=data.xlsx");workbook.write(response.getOutputStream());}
 // 获取前面列集合对应的值字符串
public String getPrexStr(String... strs){StringBuilder sb = new StringBuilder();for(String str:strs){if(!ObjectUtils.isEmpty(str)){sb.append("[");sb.append(str);sb.append("]");}else{sb.append("[");sb.append(str);sb.append("]");}}return sb.toString();}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/74707.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AI比人脑更强,因为被植入思维模型【42】思维投影思维模型

giszz的理解&#xff1a;本质和外在。我们的行为举止&#xff0c;都是我们的内心的表现。从外边可以看内心&#xff0c;从内心可以判断外在。曾国藩有&#xff17;个识人的方法&#xff0c;大部分的人在他的面前如同没穿衣服一样。对于我们自身的启迪&#xff0c;我认为有四点&…

Spring Boot 打印日志

1.通过slf4j包中的logger对象打印日志 Spring Boot内置了日志框架slf4j&#xff0c;在程序中调用slf4j来输出日志 通过创建logger对象打印日志&#xff0c;Logger 对象是属于 org.slf4j 包下的不要导错包。 2.日志级别 日志级别从高到低依次为: FATAL:致命信息&#xff0c;表…

【IOS webview】源代码映射错误,页面卡住不动

报错场景 safari页面报源代码映射错误&#xff0c;页面卡住不动。 机型&#xff1a;IOS13 技术栈&#xff1a;react 其他IOS也会报错&#xff0c;但不影响页面显示。 debug webpack配置不要GENERATE_SOURCEMAP。 解决方法&#xff1a; GENERATE_SOURCEMAPfalse react-app…

ES中经纬度查询geo_point

0. ES版本 6.x版本 1. 创建索引 PUT /location {"settings": {"number_of_shards": 1,"number_of_replicas": 0},"mappings": {"location": {"properties": {"id": {"type": "keywor…

OpenCV界面编程

《OpenCV计算机视觉开发实践&#xff1a;基于Python&#xff08;人工智能技术丛书&#xff09;》(朱文伟&#xff0c;李建英)【摘要 书评 试读】- 京东图书 OpenCV的Python开发环境搭建(Windows)-CSDN博客 OpenCV也支持有限的界面编程&#xff0c;主要是针对窗口、控件和鼠标…

GOC L2 第五课模运算和周期二

课堂回顾&#xff1a; 求取余数的过程叫做模运算 每轮的动作都是重复的&#xff0c;我们称这个过程位周期。 课堂学习&#xff1a; 剩余计算器 秋天到了&#xff0c;学校里的苹果熟了&#xff0c;太乙老师&#xff0c;想让哪吒帮忙设计一个计算器&#xff0c;看每个小朋友能分…

54.大学生心理健康管理系统(基于springboot项目)

目录 1.系统的受众说明 2.相关技术 2.1 B/S结构 2.2 MySQL数据库 3.系统分析 3.1可行性分析 3.1.1时间可行性 3.1.2 经济可行性 3.1.3 操作可行性 3.1.4 技术可行性 3.1.5 法律可行性 3.2系统流程分析 3.3系统功能需求分析 3.4 系统非功能需求分析 4.系统设计…

Redis 除了数据类型外的核心功能 的详细说明,包含事务、流水线、发布/订阅、Lua 脚本的完整代码示例和表格总结

以下是 Redis 除了数据类型外的核心功能 的详细说明&#xff0c;包含事务、流水线、发布/订阅、Lua 脚本的完整代码示例和表格总结&#xff1a; 1. Redis 事务&#xff08;Transactions&#xff09; 功能描述 事务通过 MULTI 和 EXEC 命令将一组命令打包执行&#xff0c;保证…

STM32F103C8T6单片机硬核原理篇:讨论GPIO的基本原理篇章1——只讨论我们的GPIO简单输入和输出

目录 前言 输出时的GPIO控制部分 标准库是如何操作寄存器完成GPIO驱动的初始化的&#xff1f; 问题1&#xff1a;如何掌握GPIO的编程细节——跟寄存器如何打交道 问题2&#xff1a;哪些寄存器&#xff0c;去哪里找呢&#xff1f; 问题三&#xff0c;寄存器的含义&#xff…

前端布局难题:父元素padding导致子元素无法全屏?3种解决方案

大家好&#xff0c;我是一诺。今天要跟大家分享一个我在实际项目中经常用到的CSS技巧——如何让子元素突破父元素的padding限制&#xff0c;实现真正的全屏宽度效果。 为什么会有这个需求&#xff1f; 记得我刚入行的时候&#xff0c;接到一个需求&#xff1a;要在内容区插入…

当网页受到DDOS网络攻击有哪些应对方法?

分布式拒绝服务攻击也是人们较为熟悉的DDOS攻击&#xff0c;这类攻击会通过大量受控制的僵尸网络向目标服务器发送请求&#xff0c;以此来消耗服务器中的资源&#xff0c;致使用户无法正常访问&#xff0c;当网页受到分布式拒绝服务攻击时都有哪些应对方法呢&#xff1f; 建立全…

LeNet-5简介及matlab实现

文章目录 一、LeNet-5网络结构简介二、LeNet-5每一层的实现原理2.1. 第一层 (C1) &#xff1a;卷积层&#xff08;Convolution Layer&#xff09;2.2. 第二层 (S2) &#xff1a;池化层&#xff08;Pooling Layer&#xff09;2.3. 第三层&#xff08;C3&#xff09;&#xff1a;…

【LLM】MCP(Python):实现 stdio 通信的Client与Server

本文将详细介绍如何使用 Model Context Protocol (MCP) 在 Python 中实现基于 STDIO 通信的 Client 与 Server。MCP 是一个开放协议&#xff0c;它使 LLM 应用与外部数据源和工具之间的无缝集成成为可能。无论你是构建 AI 驱动的 IDE、改善 chat 交互&#xff0c;还是构建自定义…

Docker 安装 Elasticsearch 教程

目录 一、安装 Elasticsearch 二、安装 Kibana 三、安装 IK 分词器 四、Elasticsearch 常用配置 五、Elasticsearch 常用命令 一、安装 Elasticsearch &#xff08;一&#xff09;创建 Docker 网络 因为后续还需要部署 Kibana 容器&#xff0c;所以需要让 Elasticsearch…

Swagger @ApiOperation

ApiOperation 注解并非 Spring Boot 自带的注解&#xff0c;而是来自 Swagger 框架&#xff0c;Swagger 是一个规范且完整的框架&#xff0c;用于生成、描述、调用和可视化 RESTful 风格的 Web 服务&#xff0c;而 ApiOperation 主要用于为 API 接口的操作添加描述信息。以下为…

【奇点时刻】GPT4o新图像生成模型底层原理深度洞察报告(篇2)

由于上一篇解析深度不足&#xff0c;经过查看学习相关论文&#xff0c;以下是一份对 GPT-4o 最新的图像生成模型 的深度梳理与洞察&#xff0c;从模型原理到社区解读、对比传统扩散模型&#xff0c;再到对未来趋势的分析。为了便于阅读&#xff0c;整理成以下七个部分&#xff…

C# 窗体应用(.FET Framework ) 打开文件操作

一、 打开文件或文件夹加载数据 1. 定义一个列表用来接收路径 public List<string> paths new List<string>();2. 打开文件选择一个文件并将文件放入列表中 OpenFileDialog open new OpenFileDialog(); // 过滤 open.Filter "(*.jpg;*.jpge;*.bmp;*.png…

Scala 面向对象编程总结

​​​抽象属性和抽象方法 基本语法 定义抽象类&#xff1a;abstract class Person{} //通过 abstract 关键字标记抽象类定义抽象属性&#xff1a;val|var name:String //一个属性没有初始化&#xff0c;就是抽象属性定义抽象方法&#xff1a;def hello():String //只声明而没…

人工智能赋能工业制造:智能制造的未来之路

一、引言 随着人工智能技术的飞速发展&#xff0c;其应用场景不断拓展&#xff0c;从消费电子到医疗健康&#xff0c;从金融科技到交通运输&#xff0c;几乎涵盖了所有行业。而工业制造作为国民经济的支柱产业&#xff0c;也在人工智能的浪潮中迎来了深刻的变革。智能制造&…

元宇宙概念下,UI 设计如何打造沉浸式体验?

一、元宇宙时代UI设计的核心趋势 在元宇宙概念下&#xff0c;UI设计的核心目标是打造沉浸式体验&#xff0c;让用户在虚拟世界中感受到身临其境的交互效果。以下是元宇宙时代UI设计的几个核心趋势&#xff1a; 沉浸式体验设计 元宇宙的核心是提供沉浸式体验&#xff0c;UI设计…