news 2026/10/4 1:46:24

Apache Fesod POJO 写入实战:按参数筛选列、控制列顺序与无模型导出

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Apache Fesod POJO 写入实战:按参数筛选列、控制列顺序与无模型导出
  • 后端

【免费下载链接】fesod

Fast. Easy. Done. Processing spreadsheets without worrying about large files causing OOM.

项目地址:https://gitcode.com/gh_mirrors/fast/fesod
点击查看免费下载

Apache Fesod(Incubating)是专注于大数据量电子表格处理的开源框架,其核心入口FesodSheet提供了一套流式、低内存占用的读写 API。本篇技术指南围绕 Fesod 的POJO 写入主题展开,覆盖三类高频实战场景:按参数动态导出指定列、精确控制列的写入顺序、以及不创建实体类的无模型(No Model)导出。读完本文,你将掌握excludeColumnFieldNames/includeColumnFieldNames/orderByIncludeColumn/@ExcelProperty(index)等核心参数与注解的用法与底层语义,并能在自己的导出接口中直接落地。

文中所有示例均可在仓库中找到对应源码佐证:核心写入口位于 FesodSheet.java,写参数定义于 WriteBasicParameter.java,列筛选与重排的底层实现在 ClassUtils.java,配套测试见 ExcludeOrIncludeDataTest.java。

准备工作:FesodSheet.write()与 POJO 约定

开始之前,先理解写入口的构建链。FesodSheet提供了write(File)、write(String pathName)、write(OutputStream)三个重载,以及各自带Class head的版本,最终都返回ExcelWriterBuilder,由 FesodSheet.java 中file(...).headIfNotNull(...)完成初始化:

FesodSheet.write(fileName, DemoData.class) // 指定输出文件与模型 Class .sheet() // 进入 sheet 级配置 .doWrite(data()); // 写入数据列表

POJO 模型通过@ExcelProperty注解声明表头与列位置。以 Simple Writing 章节中的DemoData为例,其字段为string、date、doubleData:

@Getter @Setter @EqualsAndHashCode public class DemoData { @ExcelProperty("String Title") private String string; @ExcelProperty("Date Title") private Date date; @ExcelProperty("Number Title") private Double doubleData; @ExcelIgnore private String ignore; // 被忽略的字段 }

@ExcelProperty注解由 ExcelProperty.java 定义,包含四个核心属性:

属性默认值说明
value(){""}表头名称,可传多个值实现多级表头(写入时自动合并,读取时取最后一个)
index()-1列的绝对位置,从 0 开始;为-1时按 Java 类字段声明顺序排序
order()Integer.MAX_VALUE列排序权重
converter()AutoConverter.class强制该字段使用指定类型转换器

需要特别留意注解注释中的优先级约定:index > order > 默认排序,即显式声明了index的列,其位置以index为准。

根据参数动态导出指定列

实际业务中,"导出全部列" 往往不够用:接口可能要求按用户勾选动态决定输出哪些列。Fesod 提供两种互补的集合参数,且集合中装的是POJO 字段名(field name),而不是表头标题。

排除指定列:excludeColumnFieldNames

除集合中列出的字段外,其余字段全部写出:

@Test public void excludeColumnWrite() { String fileName = "excludeColumnFieldWrite" + System.currentTimeMillis() + ".xlsx"; Set<String> excludeColumns = Collections.singleton("date"); FesodSheet.write(fileName, DemoData.class) .excludeColumnFieldNames(excludeColumns) .sheet() .doWrite(data()); }

结果:date列消失,string与doubleData两列保留:

AB
1String TitleNumber Title
2String00.56
3String10.56
4String20.56
⋮……
11String90.56

仅导出指定列:includeColumnFieldNames

只写出集合中列出的字段:

@Test public void includeColumnWrite() { String fileName = "includeColumnFieldWrite" + System.currentTimeMillis() + ".xlsx"; Set<String> includeColumns = Collections.singleton("date"); FesodSheet.write(fileName, DemoData.class) .includeColumnFieldNames(includeColumns) .sheet() .doWrite(data()); }

结果:仅保留date一列:

A
1Date Title
22026-07-31 20:50:23
⋮…
112026-07-31 20:50:23

底层原理:字段过滤发生在头部构建阶段

这两个参数并不是写入时临时裁剪数据,而是在模型头部(Head)构建阶段就完成了字段的取舍。以 ClassUtils.java 的doDeclaredFields为例:

  1. 通过FieldUtils.resolveAllFields(clazz)解析出类的全部字段,剔除@ExcelIgnore标注的字段;
  2. 判断写参数中是否存在excludeColumnFieldNames/excludeColumnIndexes/includeColumnFieldNames/includeColumnIndexes任一配置;
  3. 若命中writeHolder.ignore(fieldName, index),该字段被加入忽略集并从有序字段图中移除;
  4. 剩余字段重新分配连续的列索引,得到最终FieldCache。

注意:exclude与include两套参数中,WriteBasicParameter(见 WriteBasicParameter.java)同时定义了基于列索引的excludeColumnIndexes/includeColumnIndexes与基于字段名的两个集合版本,可按需混用。Builder 上同时存在excludeColumnFiledNames(拼写错误)与excludeColumnFieldNames两个方法,前者已标记@Deprecated,参见 AbstractExcelWriterParameterBuilder.java,新代码请统一使用excludeColumnFieldNames/includeColumnFieldNames。

测试 ExcludeOrIncludeDataTest.java 的excludeFieldName用例验证了该行为:排除column1、column3、column4后,回读结果仅剩column2一列。

指定列顺序写入

默认情况下,POJO 模型各列按字段声明顺序依次排列。当需要精细控制列位置时,Fesod 提供了注解与参数两种手段。

方式一:@ExcelProperty(index = n)指定绝对列位

@Getter @Setter @EqualsAndHashCode public class IndexData { @ExcelProperty(value = "String Title", index = 0) private String string; @ExcelProperty(value = "Date Title", index = 1) private Date date; @ExcelProperty(value = "Number Title", index = 3) private Double doubleData; }

写入时无需任何额外参数:

@Test public void indexWrite() { String fileName = "indexWrite" + System.currentTimeMillis() + ".xlsx"; FesodSheet.write(fileName, IndexData.class) .sheet() .doWrite(data()); }

结果:string在第 0 列、date在第 1 列、doubleData在第 3 列:

ABCD
1String TitleDate Title(空)Number Title
2String02026-07-31 20:50:23(空)0.56

重要语义提醒:index是从 0 开始的绝对列位置,而不是排序键。上面IndexData声明了0、1、3,所以第 2 列(C 列)刻意留空,且这个空位会原样保留在输出文件中。若希望三列紧挨着排,应依次编号0、1、2。

从源码看,index的解析遵循 ExcelProperty.java 中声明的优先级:index > order > 默认排序;index为-1时回退到类字段顺序。

方式二:includeColumnFieldNames的组合顺序

当与includeColumnFieldNames配合使用时,需要注意一个反直觉的默认行为:列的输出顺序是 POJO 字段的声明顺序,而不是集合的遍历顺序。即使集合把doubleData写在string前面,输出时string依然排第一:

@Test public void includeColumnOrderWrite() { String fileName = "includeColumnFieldWrite" + System.currentTimeMillis() + ".xlsx"; Set<String> includeColumns = new LinkedHashSet<>(Arrays.asList("doubleData", "string")); FesodSheet.write(fileName, DemoData.class) .includeColumnFieldNames(includeColumns) .sheet() .doWrite(data()); }

结果:string在前、doubleData在后(与DemoData字段声明顺序一致,而非集合顺序):

AB
1String TitleNumber Title
2String00.56

方式三:orderByIncludeColumn(true)按集合顺序输出

若希望列顺序与传入集合的顺序完全一致,追加.orderByIncludeColumn(true)即可:

@Test public void orderByIncludeColumnWrite() { String fileName = "includeColumnFieldWrite" + System.currentTimeMillis() + ".xlsx"; Set<String> includeColumns = new LinkedHashSet<>(Arrays.asList("doubleData", "string")); FesodSheet.write(fileName, DemoData.class) .includeColumnFieldNames(includeColumns) .orderByIncludeColumn(true) .sheet() .doWrite(data()); }

结果:doubleData反超string,列顺序变为集合顺序:

AB
1Number TitleString Title
20.56String0

关键前提:orderByIncludeColumn依赖集合具备稳定的迭代顺序,请使用LinkedHashSet或List,不要使用HashSet——后者的迭代顺序不确定,会导致列顺序随机化。

底层原理:resortField的重排逻辑

orderByIncludeColumn的语义由 ClassUtils.java 的resortField实现,其注释明确:该方法仅在includeColumnFieldNames/includeColumnIndexes有值且orderByIncludeColumn为true时生效:

  • 当配置的是字段名集合时,遍历集合为每个字段名分配递增的临时索引(filedIndexMap),随后重建sortedFieldMap:集合中第 N 个字段落在第 N 列;
  • 当配置的是索引集合时,同理将原始列索引映射为连续的临时索引;
  • 若重排后字段的新位置与旧位置不同,旧的indexFieldMap条目会被移除,即注解指定的顺序在此场景下失效,以集合顺序为准。

测试 ExcludeOrIncludeDataTest.java 的includeFieldNameOrder用例验证:集合按column4、column2、column3传入后,回读的列顺序恰为column4 → column2 → column3,与声明顺序无关。

不创建对象的无模型写入

如果数据来自数据库动态查询、列结构在运行期才能确定,此时不必强行设计实体类,可以用List<List<String>>定义表头、List<List<Object>>定义数据,直接写入。

代码示例

@Test public void noModelWrite() { String fileName = "noModelWrite" + System.currentTimeMillis() + ".xlsx"; FesodSheet.write(fileName) .head(head()) // 动态表头 .sheet("Write Without Object") .doWrite(dataList()); } private List<List<String>> head() { return Arrays.asList( Collections.singletonList("String Title"), Collections.singletonList("Number Title"), Collections.singletonList("Date Title")); } private List<List<Object>> dataList() { List<List<Object>> list = new ArrayList<>(); for (int i = 0; i < 10; i++) { list.add(Arrays.asList("String" + i, 0.56, new Date())); } return list; }

结果:三列表头 + 十行数据:

ABC
1String TitleNumber TitleDate Title
2String00.562026-07-31 20:50:23
3String10.562026-07-31 20:50:23
⋮………
11String90.562026-07-31 20:50:23

要点说明:

  • head()返回List<List<String>>,内层 List 代表一行表头:内层只有一个元素即单级表头;若内层含多个元素,则构成多级(合并)表头,配合automaticMergeHead(默认true,见 ExcelWriterBuilder.java 与 AbstractExcelWriterParameterBuilder.java)自动合并单元格;
  • FesodSheet.write(fileName)不传模型 Class 时,通过.head(head())手动注入表头;
  • 数据行的每个元素会走内置的类型转换器(字符串原样写出、Double输出为数值单元格、Date按默认日期格式写出),因此dataList使用List<List<Object>>容纳异构类型;
  • .sheet("Write Without Object")指定了 sheet 名称,等价于sheet(null, sheetName)重载。

小结

需求推荐手段
动态剔除若干列excludeColumnFieldNames(Collection<String>),集合装字段名
动态仅保留若干列includeColumnFieldNames(Collection<String>),集合装字段名
固定精确的列位置@ExcelProperty(value = "...", index = n),index从 0 起为绝对列位,支持留空列
让列顺序跟随传入集合includeColumnFieldNames(...).orderByIncludeColumn(true),配合LinkedHashSet/List
不建实体类直接导出FesodSheet.write(path).head(List<List<String>>).sheet().doWrite(List<List<Object>>)

这三类能力覆盖了从"固定模型导出"到"运行时动态导出"的完整谱系:字段级过滤让导出接口可以按用户勾选裁剪列,index与orderByIncludeColumn让列顺序可控、可解释,无模型写入则彻底解耦了实体类设计与动态列场景。若要继续深入,可进一步阅读 Simple Writing 了解基础写入全貌,或在 write 目录下查看表头、格式、样式等进阶章节;对读取端 POJO 映射感兴趣的话,可参考 POJO 读取。

  • 后端

【免费下载链接】fesod

Fast. Easy. Done. Processing spreadsheets without worrying about large files causing OOM.

项目地址:https://gitcode.com/gh_mirrors/fast/fesod
点击查看免费下载

相关推荐

上一篇:WebdriverIO与GitLab CI集成:完整测试流水线配置与并行执行优化
下一篇:终极Godot逆向工程工具:gdsdecomp完整解析与实战指南

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/4 1:46:15

Linux下C语言真实执行机制:编译、内存、调试全链路解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/4 1:45:46

QuickBlue:面向Java微服务的AI应用底座实战指南

1. QuickBlue 是什么&#xff0c;为什么企业需要一个“AI 应用底座”QuickBlue 不是一个玩具级 Demo 工具&#xff0c;也不是某个厂商包装出来的营销概念。它是一套经过真实产线验证、面向中大型 Java 微服务架构团队设计的可开箱即用的 AI 原生应用支撑平台。我带过三个不同行…

作者头像 李华
网站建设 2026/10/4 1:44:56

使用 Universal Ctags 为 Scheme 源码生成标签(ctags-lang-scheme 指南)

开发工具CLI 【免费下载链接】ctags A maintained ctags implementation 项目地址&#xff1a; https://gitcode.com/gh_mirrors/ct/ctags 点击查看 免费下载 Universal Ctags 为 Scheme&#xff08;包括 Racket、Guile、Gauche 等方言&#xff09;提供了专门的内置解析器。本…

作者头像 李华