Easyexcel 导出数据 一对多关系导出数据集合

2023-11-07

客户要求

要求导出的表格如图
在这里插入图片描述
实现这样表格 很多人会想到动态表头,easypoi可以直接实现,但是我用的是easyexcel,而easyexcel自身并没有提供自动合并的功能所以还是需要自己来合并。

代码如下

首先我们来看下将嵌套数据平铺,不进行合并导出的Excel

在这里插入图片描述

实现思路

首先我们得把原来嵌套的订单商品信息给平铺了,创建一个专门的导出对象OrderData,包含订单和商品信息,二级表头可以通过设置@ExcelProperty的value为数组来实现

@Data
@AllArgsConstructor
@NoArgsConstructor
public class OrderData {


    @ExcelProperty(value = "订单编码")
    @ColumnWidth(20)
    @CollectCustomMerge(needMerge = true, isPk = true)
    private String orderSn;
    @ExcelProperty(value = "创建时间")
    @ColumnWidth(20)
    @DateTimeFormat("yyyy-MM-dd")
    @CollectCustomMerge(needMerge = true)
    private Date createTime;
    @ExcelProperty(value = "收货地址")
    @CollectCustomMerge(needMerge = true)
    @ColumnWidth(20)
    private String receiverAddress;
    @ExcelProperty(value = {"商品信息", "商品编码"})
    @ColumnWidth(20)
    private String productSn;
    @ExcelProperty(value = {"商品信息", "商品名称"})
    @ColumnWidth(20)
    private String name;
    @ExcelProperty(value = {"商品信息", "商品标题"})
    @ColumnWidth(30)
    private String subTitle;

}

创建一个自定义注解CollectCustomMerge,用于标记哪些属性需要合并,哪个是主键

/**
 * 自定义注解,用于判断是否需要合并以及合并的主键
 * 标记哪些属性需要合并,哪个是主键
 */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface CollectCustomMerge {

    /**
     * 是否需要合并单元格
     */
    boolean needMerge() default false;

    /**
     * 是否是主键,即该字段相同的行合并
     */
    boolean isPk() default false;
}

创建自定义单元格合并策略类CustomMergeStrategy,当Excel中两列主键相同时,合并被标记需要合并的列


import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.write.handler.RowWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import com.easyexcle.demo.easyExcel.annotion.CollectCustomMerge;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * @Author: lkz
 * @Title: CustomMergeStrategy
 * @Description: 自定义单元格合并策略类CustomMergeStrategy,当Excel中两列主键相同时,合并被标记需要合并的列
 * @Date: 2023/7/5 9:16
 */
public class CustomMergeStrategy implements RowWriteHandler {
    /**
     * 主键下标
     */
    private Integer pkIndex;

    /**
     * 需要合并的列的下标集合
     */
    private List<Integer> needMergeColumnIndex = new ArrayList<>();

    /**
     * DTO数据类型
     */
    private Class<?> elementType;

    public CustomMergeStrategy(Class<?> elementType) {
        this.elementType = elementType;
    }

    @Override
    public void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Integer rowIndex, Integer relativeRowIndex, Boolean isHead) {

    }

    @Override
    public void afterRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer relativeRowIndex, Boolean isHead) {

    }

    @Override
    public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer relativeRowIndex, Boolean isHead) {
        // 如果是标题,则直接返回
        if (isHead) {
            return;
        }

        // 获取当前sheet
        Sheet sheet = writeSheetHolder.getSheet();

        // 获取标题行
        Row titleRow = sheet.getRow(0);

        if (null == pkIndex) {
            this.lazyInit(writeSheetHolder);
        }

        // 判断是否需要和上一行进行合并
        // 不能和标题合并,只能数据行之间合并
        if (row.getRowNum() <= 1) {
            return;
        }
        // 获取上一行数据
        Row lastRow = sheet.getRow(row.getRowNum() - 1);
        // 将本行和上一行是同一类型的数据(通过主键字段进行判断),则需要合并
        if (lastRow.getCell(pkIndex).getStringCellValue().equalsIgnoreCase(row.getCell(pkIndex).getStringCellValue())) {
            for (Integer needMerIndex : needMergeColumnIndex) {
                CellRangeAddress cellRangeAddress = new CellRangeAddress(row.getRowNum() - 1, row.getRowNum(),
                        needMerIndex, needMerIndex);
                sheet.addMergedRegionUnsafe(cellRangeAddress);
            }
        }
    }

    /**
     * 初始化主键下标和需要合并字段的下标
     */
    private void lazyInit(WriteSheetHolder writeSheetHolder) {

        // 获取当前sheet
        Sheet sheet = writeSheetHolder.getSheet();

        // 获取标题行
        Row titleRow = sheet.getRow(0);
        // 获取DTO的类型
        Class<?> eleType = this.elementType;

        // 获取DTO所有的属性
        Field[] fields = eleType.getDeclaredFields();

        // 遍历所有的字段,因为是基于DTO的字段来构建excel,所以字段数 >= excel的列数
        for (Field theField : fields) {
            // 获取@ExcelProperty注解,用于获取该字段对应在excel中的列的下标
            ExcelProperty easyExcelAnno = theField.getAnnotation(ExcelProperty.class);
            // 为空,则表示该字段不需要导入到excel,直接处理下一个字段
            if (null == easyExcelAnno) {
                continue;
            }
            // 获取自定义的注解,用于合并单元格
            CollectCustomMerge collectCustomMerge = theField.getAnnotation(CollectCustomMerge.class);

            // 没有@CustomMerge注解的默认不合并
            if (null == collectCustomMerge) {
                continue;
            }

            for (int index = 0; index < fields.length; index++) {
                Cell theCell = titleRow.getCell(index);
                // 当配置为不需要导出时,返回的为null,这里作一下判断,防止NPE
                if (null == theCell) {
                    continue;
                }
                // 将字段和excel的表头匹配上
                if (easyExcelAnno.value()[0].equalsIgnoreCase(theCell.getStringCellValue())) {
                    if (collectCustomMerge.isPk()) {
                        pkIndex = index;
                    }

                    if (collectCustomMerge.needMerge()) {
                        needMergeColumnIndex.add(index);
                    }
                }
            }
        }

        // 没有指定主键,则异常
        if (null == this.pkIndex) {
            throw new IllegalStateException("使用@CustomMerge注解必须指定主键");
        }

    }
}

测试最终效果

public class CollectTest {


    public static void main(String[] args) {
    
        List<OrderData> orderDataList=new ArrayList<>();
        orderDataList.add(new OrderData("1",new Date(),"南京","001","商品名称1","标题1"));
        orderDataList.add(new OrderData("1",new Date(),"南京","001","商品名称2","标题2"));
        orderDataList.add(new OrderData("2",new Date(),"北京","002","商品名称02","标题02"));
        orderDataList.add(new OrderData("2",new Date(),"北京","003","商品名称03","标题1"));
        orderDataList.add(new OrderData("1",new Date(),"南京","001","商品名称3","标题3"));
        orderDataList.add(new OrderData("3",new Date(),"上海","001","商品名称3","标题3"));
        orderDataList.add(new OrderData("3",new Date(),"天津","001","商品名称302","标题302"));
        orderDataList.add(new OrderData("3",new Date(),"上海","001","商品名称303","标题303"));
        orderDataList.add(new OrderData("4",new Date(),"杭州","001","商品名称4","标题4"));
        orderDataList.add(new OrderData("5",new Date(),"杭州","001","商品名称5","标题5"));


        EasyExcel.write("D:/pdf/CollectExport.xlsx").head(OrderData.class)
                .registerWriteHandler(new CustomMergeStrategy(OrderData.class))
                .sheet().doWrite(orderDataList);

    }

}

注意事项

在这里插入图片描述

效果

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Easyexcel 导出数据 一对多关系导出数据集合 的相关文章

随机推荐

  • hugo+GitHub搭建博客网站

    准备工具 Git 设置 Linux命令行下载就可以 arch Linux安装命令 sudo pacman S git Debian 和 Ubuntu 安装命令 sudo apt get install git Windows 的Git 下载
  • 西门子S7通信协议以及JAVA版的实现

    西门子S7协议实现 采用java的方式实现西门子S7协议 链接地址 iot communication github https github com xingshuangs iot communication gitee https gi
  • Leetcode437. 路径总和 III(C语言)

    Leetcode437 路径总和 III C语言 数据结构 树 算法与数据结构参考 题目 给定一个二叉树 找出路径和等于给定数值的路径总数 路径不需要从根节点开始 也不需要在叶子节点结束 路径方向必须向下 二叉树不超过1000个节点 且节点
  • Vue 运行后自动打开浏览器的解决办法(vuecli2和vuecli3+的区别)

    一 Vue JavaScript 需要项目npm run dev运行后自动打开浏览器 VueCli2 1 1 版本说明 vue 2 5 2 vue router 3 0 1 vuecli2 1 2 修改项目目录下 config index
  • 验证码识别+爆破

    碰到比较简单 很好辨认的验证码可以尝试此种方法爆破 审查元素 找到验证码对应URL 复制到域名后面访问一下 确定是验证码URL 然后 打开工具PKAV HTTP FUzzer 开始尝试自动识别验证码 工具下载地址 https sec kim
  • svn使用记录

    File not found transaction 109 3q path trunk Setting Config cs You have to update your working copy first 好像是本地的 svn库中包含
  • 大模型基础03:Embedding 实战本地知识问答

    大模型基础 Embedding 实战本地知识问答 Embedding 概述 知识在计算机内的表示是人工智能的核心问题 从数据库 互联网到大模型时代 知识的储存方式也发生了变化 在数据库中 知识以结构化的数据形式储存在数据库中 需要机器语言
  • js实现轮播图原理及示例(转)

    网上有很多的例子介绍 在这里我所做的 无缝滚动 就是 通过改变元素的left值让图片呈现左右滚动的效果 我们首先看一下 div css 的结构样式 div css代码
  • 计算机二级excel评选投票,计算机二级-31-2.电子表格题(小刘-评选投票)

    1 首先查看本节课的课程题目 要求如图所示 2 然后打开 考试素材 具体目录如图所示 3 然后是 第一小题 修改文件名为Excel 进行另存 默认一般跟素材文件夹目录相同 4 第二小题 题目要求如图所示 5 首先检查 代码地址是否对应 然后
  • 正则表达式攻略

    第一章 正则表达式字符匹配攻略 正则表达式是匹配模式 要么匹配字符 要么匹配位置 请记住这句话 然而关于正则如何匹配字符的学习 大部分人都觉得这块比较杂乱 毕竟元字符太多了 看起来没有系统性 不好记 本章就解决这个问题 内容包括 两种模糊匹
  • Java用File完成硬盘文件的增删查改操作

    我的项目环境是eclipse 如果用的是idea的话最好创建好一个同类名的 class文件 因为字符编码不同 我用的是GBK 如果报错或者出现中文注释不出现的话 最好搜一下idea怎么改字符编码 package com bili demo3
  • siebel escript入门——基础知识一

    1 检测escript变量是否已定义 var test if typeof test undefined TheApplication RaiseErrorText test is undefined 2 escript的基本数据类型 es
  • 立式oled拼接屏有哪些产品优点?

    葫芦岛oled拼接屏是一种高清晰度的显示屏 由多个oled屏幕拼接而成 它可以用于广告牌 展览 演示 会议等场合 具有高亮度 高对比度 高色彩饱和度 高刷新率等优点 能够吸引人们的眼球 提高信息传递效果 葫芦岛oled拼接屏的优点主要有以下
  • 02node.js-◆ 模块化的基本概念 ◆ Node.js 中模块的分类 ◆ npm与包 ◆ 模块的加载机制

    学习内容 模块化的基本概念 Node js 中模块的分类 npm与包 模块的加载机制 1 模块化的基本概念 1 1 什么是模块化 模块化是指解决一个复杂问题时 自顶向下逐层把系统划分成若干模块的过程 对于整个系统来说 模块是可组合 分解和更
  • linux中,如何使用tar进行解压缩

    linux中 如何使用tar进行解压缩 环境 windows 7 virtualboax fedora 15 kde 可以使用tar命令解压缩 tar gz文件 下面以解压缩qt源文件举例 1 在windows中将qt源文件拷贝到共享文件夹
  • Python基础语法【4】—— 结构数据类型之列表

    文章目录 一 创建列表 1 使用 直接创建列表 2 使用list 函数创建列表 二 访问列表元素 1 使用索引方式访问列表元素 2 使用切片方式访问列表元素 三 添加元素到列表 1 使用append 方法添加元素 2 使用extend 方法
  • nvidia 专业显卡解码能力

    专业显卡问题 p620 解码 有时我们经常遇到专业显卡 专业显卡和非专业显卡在使用opengl 等底层调用时表现不一样 值得注意的是 专业显卡解码能力到了p400 以上才有显著的提升 p620 家族为开始又有提升 p620 不支持vp8 解
  • xml 入门 dtd

    xml dtd的格式 相
  • 入门文献复现——Murphy C K——Combining belief functions when evidence conflicts

    作者Murphy提出了综合平均法来组合多个BOE 大体的步骤如下 1 将给定的BOE进行平均 获得各个BPA的平均质量averageMass 2 利用Dempster的组合规则将 1 求得的平均质量进行组合 并且组合 n 1 次 n为BOE
  • Easyexcel 导出数据 一对多关系导出数据集合

    客户要求 要求导出的表格如图 实现这样表格 很多人会想到动态表头 easypoi可以直接实现 但是我用的是easyexcel 而easyexcel自身并没有提供自动合并的功能所以还是需要自己来合并 代码如下 首先我们来看下将嵌套数据平铺 不