EasyExcel多层表头

EasyExcel实现多层表头:注解与列表两种方式详解

在复杂报表场景中,多层表头(如 “基础信息” 包含 “姓名”“手机号” 等子列)能更清晰地组织数据。EasyExcel 提供了简洁的 API 支持多层表头,本文将详细讲解两种实现方式(注解方式和列表方式),并对比其适用场景,帮助你快速实现类似 Excel 数据透视表的表头结构。

多层表头的核心原理

多层表头本质是多级列标题的合并与嵌套,例如 “基础信息” 作为一级标题,其下包含 “姓名”“手机号” 等二级标题。EasyExcel 通过以下方式实现:

  • 层级定义:用数组或列表的嵌套结构表示表头层级(如 {"基础信息", "姓名"} 表示两级表头);
  • 自动合并:相同的上级标题会自动合并单元格(如 “基础信息” 会跨列合并其所有子列)。

环境准备

引入 EasyExcel 依赖(兼容 2.x 和 3.x 版本):

<dependency>  
    <groupId>com.alibaba</groupId>  
    <artifactId>easyexcel</artifactId>  
    <version>3.3.0</version>  
</dependency>

实现方式详解

注解方式(推荐用于固定表头)

通过 @ExcelProperty 注解的 value 属性定义多层表头,适用于表头结构固定的场景(如实体类与表头一一对应)。

定义实体类

在实体类的字段上使用 @ExcelProperty(value = {一级标题, 二级标题, ...}) 定义多层表头:

import com.alibaba.excel.annotation.ExcelProperty;  
import lombok.Data;  

@Data  
public class UserInfo {  
    // 二级表头:基础信息 -> 姓名  
    @ExcelProperty(value = {"基础信息", "姓名"}, index = 0)  
    private String name;  

    // 二级表头:基础信息 -> 手机号  
    @ExcelProperty(value = {"基础信息", "手机号"}, index = 1)  
    private String phone;  

    // 二级表头:基础信息 -> 年龄  
    @ExcelProperty(value = {"基础信息", "年龄"}, index = 2)  
    private Integer age;  

    // 二级表头:其他信息 -> 教育经历  
    @ExcelProperty(value = {"其他信息", "教育经历"}, index = 3)  
    private String education;  

    // 二级表头:其他信息 -> 工作经历  
    @ExcelProperty(value = {"其他信息", "工作经历"}, index = 4)  
    private String workExperience;  
}
  • value 属性:数组元素依次表示从一级到 N 级的表头名称;
  • index 属性:指定列的顺序(0 开始),确保表头按预期排列。
生成 Excel

直接将实体类作为表头类传入 EasyExcel.write() 方法:

import com.alibaba.excel.EasyExcel;  
import java.util.ArrayList;  
import java.util.List;  

public class MultiLevelHeaderDemo {  
    public static void main(String[] args) {  
        String filePath = "多层表头_注解方式.xlsx";  

        // 准备测试数据(可选,此处为空列表仅生成表头)  
        List<UserInfo> dataList = new ArrayList<>();  
        // dataList.add(new UserInfo("张三", "13800138000", 25, "本科", "3年"));  

        // 写入 Excel,表头由 UserInfo 类的 @ExcelProperty 定义  
        EasyExcel.write(filePath, UserInfo.class)  
                .sheet("用户信息表")  // Sheet 名称  
                .doWrite(dataList);   // 写入数据(空列表则仅生成表头)  

        System.out.println("文件生成成功:" + filePath);  
    }  
}

列表方式(推荐用于动态表头)

通过 List<List<String>> 手动定义表头层级,适用于表头结构动态生成的场景(如根据数据库配置动态调整表头)。

构建表头列表

外层 List 代表列的集合,内层 List 代表每列的多级表头:

import com.alibaba.excel.EasyExcel;  
import java.util.ArrayList;  
import java.util.List;  

public class DynamicHeaderDemo {  
    public static void main(String[] args) {  
        String filePath = "多层表头_列表方式.xlsx";  

        // 1. 构建多层表头(List<List<String>>)  
        List<List<String>> headers = new ArrayList<>();  

        // 第一列:基础信息 -> 姓名  
        List<String> col1 = new ArrayList<>();  
        col1.add("基础信息");  
        col1.add("姓名");  
        headers.add(col1);  

        // 第二列:基础信息 -> 手机号  
        List<String> col2 = new ArrayList<>();  
        col2.add("基础信息");  
        col2.add("手机号");  
        headers.add(col2);  

        // 第三列:基础信息 -> 年龄  
        List<String> col3 = new ArrayList<>();  
        col3.add("基础信息");  
        col3.add("年龄");  
        headers.add(col3);  

        // 第四列:其他信息 -> 教育经历  
        List<String> col4 = new ArrayList<>();  
        col4.add("其他信息");  
        col4.add("教育经历");  
        headers.add(col4);  

        // 第五列:其他信息 -> 工作经历  
        List<String> col5 = new ArrayList<>();  
        col5.add("其他信息");  
        col5.add("工作经历");  
        headers.add(col5);  

        // 2. 准备数据(数据结构需与表头列顺序对应)  
        List<List<Object>> dataList = new ArrayList<>();  
        // 示例数据行:[姓名, 手机号, 年龄, 教育经历, 工作经历]  
        List<Object> dataRow = new ArrayList<>();  
        dataRow.add("张三");  
        dataRow.add("13800138000");  
        dataRow.add(25);  
        dataRow.add("本科");  
        dataRow.add("3年");  
        dataList.add(dataRow);  

        // 3. 写入 Excel,表头由 headers 定义  
        EasyExcel.write(filePath)  
                .head(headers)       // 设置动态表头  
                .sheet("用户信息表")  // Sheet 名称  
                .doWrite(dataList);  // 写入数据  

        System.out.println("文件生成成功:" + filePath);  
    }  
}
  • 数据结构List<List<Object>> 中,内层 List 的元素顺序需与表头列顺序一致;
  • 灵活性:可根据业务需求动态增删表头列(如从数据库查询表头配置后构建 headers 列表)。

扩展:三级及以上表头

EasyExcel 支持任意层级的表头,只需在注解数组或列表中增加层级即可。

示例:三级表头

注解方式
@Data  
public class OrderInfo {  
    // 三级表头:订单信息 -> 基本信息 -> 订单号  
    @ExcelProperty(value = {"订单信息", "基本信息", "订单号"}, index = 0)  
    private String orderNo;  

    // 三级表头:订单信息 -> 金额信息 -> 总金额  
    @ExcelProperty(value = {"订单信息", "金额信息", "总金额"}, index = 1)  
    private BigDecimal totalAmount;  
}
列表方式
List<List<String>> headers = new ArrayList<>();  
// 三级表头:订单信息 -> 基本信息 -> 订单号  
List<String> col1 = new ArrayList<>();  
col1.add("订单信息");  
col1.add("基本信息");  
col1.add("订单号");  
headers.add(col1);  
// ... 其他列  

两种方式的对比与适用场景

实现方式 优点 缺点 适用场景
注解方式 代码简洁,与实体类绑定,可读性强 表头固定,无法动态修改 表头结构固定的报表(如固定模板)
列表方式 支持动态生成表头,灵活性高 代码较繁琐,需手动维护列顺序 表头动态变化的场景(如配置化报表)

常见问题与解决方案

1. 表头合并异常(上级标题未跨列合并)

  • 原因:同级表头的上级标题名称不一致(如 “基础信息” 与 “基础信息 ”(含空格)被视为不同标题);
  • 解决:确保同一组的上级标题字符串完全一致(无空格、大小写差异)。

2. 数据与表头列顺序不匹配

  • 原因:注解方式中 index 属性未按顺序设置,或列表方式中数据行元素顺序与表头列顺序不一致;
  • 解决
    • 注解方式:严格按列顺序设置 index = 0, 1, 2...
    • 列表方式:数据行 List<Object> 的元素顺序必须与 headers 列表的列顺序一一对应。

3. 表头层级显示错乱

  • 原因:表头层级深度不一致(如部分列是二级,部分列是三级);
  • 解决:确保所有列的表头层级深度统一,或接受不同层级的显示效果(EasyExcel 会自动对齐空白单元格)。