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.pswp.cn/web/74707.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/74707.shtml
英文地址,請注明出處:http://en.pswp.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設計…