自定义注解实现Excel 导出
概述
一个用自定义注解实现导出字段定义的工具实现。
1. 注解定义,定义导出Excel的字段
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PoiExportField {// Label of the columnString label();// Order of the column,default 0,means the first columnint order() default 0;// If true, this field will be used to create subgroup rowsboolean subGroup() default false;// Width of the columnint width() default 20;// Alignment of the columnHorizontalAlignment align() default HorizontalAlignment.LEFT;
}
2. 实体类,使用注解定义导出字段,不导出的字段不用加注解
@Data
public class OrderVO {@PoiExportUtil.PoiExportField(label = "订单编号", order = 1, align = HorizontalAlignment.CENTER)private String orderNo;@PoiExportUtil.PoiExportField(label = "订单用户", order = 2, align = HorizontalAlignment.CENTER)private String orderUser;@PoiExportUtil.PoiExportField(label = "订单时间", order = 3, align = HorizontalAlignment.CENTER)private String orderTime;@PoiExportUtil.PoiExportField(label = "订单金额", order = 4, width = 15, align = HorizontalAlignment.RIGHT)private String orderAmount;private String orderDesc;private String orderRemark;private String orderPhone;private String orderZipCode;@PoiExportUtil.PoiExportField(label = "订单国家", subGroup = true)private String orderCountry;@PoiExportUtil.PoiExportField(label = "订单省份", subGroup = true)private String orderProvince;@PoiExportUtil.PoiExportField(label = "订单城市", order = 6)private String orderCity;@PoiExportUtil.PoiExportField(label = "详细地址", order = 7)private String orderAddressDetail;}
3. Excel导出工具类,注解定义放到工具类中,方便使用
public class PoiExportUtil {/*** Custom annotation for exporting Excel file*/@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface PoiExportField {// Label of the columnString label();// Order of the column,default 0,means the first columnint order() default 0;// If true, this field will be used to create subgroup rowsboolean subGroup() default false;// Width of the columnint width() default 20;// Alignment of the columnHorizontalAlignment align() default HorizontalAlignment.LEFT;}/*** Export data to excel file** @param list List of data* @param fileName File name* @param <T> Type of data*/public <T> void exportToExcel(List<T> list, String fileName) {Workbook workbook = new XSSFWorkbook();Sheet sheet = workbook.createSheet("Data");writeSheet(sheet, list, null, null, null);// Write to filetry (FileOutputStream fileOut = new FileOutputStream(fileName)) {workbook.write(fileOut);fileOut.flush();} catch (Exception e) {e.printStackTrace();}}/*** Write data to a sheet** @param sheet Sheet* @param dataList List of data* @param headerStyle Header style* @param subGroupStyle Subgroup style* @param dataCellStyle Data cell style* @param <T> Type of data*/public <T> void writeSheet(Sheet sheet, List<T> dataList, CellStyle headerStyle, CellStyle subGroupStyle, CellStyle dataCellStyle) {// If data list is empty, returnif (dataList == null || dataList.isEmpty()) {return;}// If styles are not provided, use default stylesif (headerStyle == null) {headerStyle = createDefaultHeaderStyle(sheet.getWorkbook());}if (subGroupStyle == null) {subGroupStyle = createDefaultSubGroupStyle(sheet.getWorkbook());}if (dataCellStyle == null) {dataCellStyle = createDefaultDataCellStyle(sheet.getWorkbook());}Field[] fields = dataList.get(0).getClass().getDeclaredFields();// Filter fields with PoiExportField annotationList<Field> annotatedFields = new ArrayList<>();// Filter fields with PoiExportField annotation and subGroup is true, for creating subgroup rowsList<Field> subGroupFields = new ArrayList<>();// Filter fields with PoiExportField annotation and sort them by order attributefor (Field field : fields) {PoiExportField annotation = field.getAnnotation(PoiExportField.class);if (annotation != null) {if (annotation.subGroup()) {subGroupFields.add(field);} else {annotatedFields.add(field);}}}// Sort fields by order attributeannotatedFields.sort(Comparator.comparingInt(field -> {PoiExportField annotation = field.getAnnotation(PoiExportField.class);return annotation.order();}));//annotated fields is empty, returnif (annotatedFields.isEmpty()) {return;}// Create header rowcreateHeaderRow(sheet, annotatedFields, headerStyle);// Create data rowscreateSheetWithData(sheet, dataList, annotatedFields, subGroupFields, subGroupStyle, dataCellStyle);}/*** Create header row** @param sheet Sheet* @param annotatedFields List of annotated fields* @param headerStyle Header style*/private void createHeaderRow(Sheet sheet, List<Field> annotatedFields, CellStyle headerStyle) {int lastRowNum = sheet.getLastRowNum();Row headerRow = sheet.createRow(lastRowNum + 1);for (int i = 0; i < annotatedFields.size(); i++) {Field field = annotatedFields.get(i);PoiExportField annotation = field.getAnnotation(PoiExportField.class);Cell headerCell = headerRow.createCell(i);headerCell.setCellValue(annotation.label());headerCell.setCellStyle(headerStyle);// Set column widthsheet.setColumnWidth(i, annotation.width() * 256);}}/*** Create data rows** @param sheet Sheet* @param dataList List of data* @param annotatedFields List of annotated fields* @param subGroupFields List of subgroup fields* @param subGroupStyle Subgroup style* @param dataCellStyle Data cell style* @param <T> Type of data*/private <T> void createSheetWithData(Sheet sheet, List<T> dataList, List<Field> annotatedFields, List<Field> subGroupFields, CellStyle subGroupStyle, CellStyle dataCellStyle) {String lastSubGroupValue = null;int rowIndex = sheet.getLastRowNum() + 1;for (T data : dataList) {// Create subgroup rowif (subGroupFields != null && !subGroupFields.isEmpty()) {String currentSubGroupValue = getSubGroupValue(data, subGroupFields);if (!currentSubGroupValue.equals(lastSubGroupValue)) {Row subGroupRow = sheet.createRow(rowIndex++);Cell subGroupCell = subGroupRow.createCell(0);subGroupCell.setCellValue(currentSubGroupValue);CellRangeAddress mergeRegion = new CellRangeAddress(rowIndex - 1, rowIndex - 1, 0, annotatedFields.size() - 1);sheet.addMergedRegion(mergeRegion);//CellRangeAddress mergeRegion = sheet.getMergedRegion(sheet.getNumMergedRegions() - 1);RegionUtil.setBorderBottom(BorderStyle.THIN, mergeRegion, sheet);RegionUtil.setBorderTop(BorderStyle.THIN, mergeRegion, sheet);RegionUtil.setBorderLeft(BorderStyle.THIN, mergeRegion, sheet);RegionUtil.setBorderRight(BorderStyle.THIN, mergeRegion, sheet);subGroupCell.setCellStyle(subGroupStyle);lastSubGroupValue = currentSubGroupValue;}}// Create data rowRow row = sheet.createRow(rowIndex++);for (int j = 0; j < annotatedFields.size(); j++) {Field field = annotatedFields.get(j);PoiExportField annotation = field.getAnnotation(PoiExportField.class);Cell cell = row.createCell(j);try {// Get field value from getter methodfield.setAccessible(true);String getterName = "get" + Character.toUpperCase(field.getName().charAt(0)) + field.getName().substring(1);Method getterMethod = data.getClass().getMethod(getterName);Object value = getterMethod.invoke(data);if (value != null) {cell.setCellValue(value.toString());}// Set cell style alignmentCellStyle cellStyle = sheet.getWorkbook().createCellStyle();cellStyle.cloneStyleFrom(dataCellStyle);cellStyle.setAlignment(annotation.align());cell.setCellStyle(cellStyle);} catch (Exception e) {e.printStackTrace();}}}}/*** Get subgroup value** @param data Data* @param subGroupFields List of subgroup fields* @param <T> Type of data* @return Subgroup value*/private <T> String getSubGroupValue(T data, List<Field> subGroupFields) {StringBuilder subGroupValue = new StringBuilder();for (Field field : subGroupFields) {try {field.setAccessible(true);Object value = field.get(data);if (value != null) {subGroupValue.append(value.toString()).append(" ");}} catch (IllegalAccessException e) {e.printStackTrace();}}return subGroupValue.toString().trim();}/*** Create and return default header style** @param workbook Workbook* @return CellStyle*/private CellStyle createDefaultHeaderStyle(Workbook workbook) {Font fontBold = workbook.createFont();fontBold.setBold(true);fontBold.setFontHeightInPoints((short) 12);CellStyle headerStyle = workbook.createCellStyle();headerStyle.setBorderTop(BorderStyle.THIN);headerStyle.setBorderBottom(BorderStyle.THIN);headerStyle.setBorderLeft(BorderStyle.THIN);headerStyle.setBorderRight(BorderStyle.THIN);headerStyle.setAlignment(HorizontalAlignment.CENTER);headerStyle.setVerticalAlignment(VerticalAlignment.CENTER);headerStyle.setWrapText(true);headerStyle.setFont(fontBold);return headerStyle;}/*** Create and return default subgroup style** @param workbook Workbook* @return CellStyle*/private CellStyle createDefaultSubGroupStyle(Workbook workbook) {// Create and return default subgroup styleFont fontBold = workbook.createFont();fontBold.setBold(true);fontBold.setFontHeightInPoints((short) 11);CellStyle subGroupStyle = workbook.createCellStyle();subGroupStyle.setBorderTop(BorderStyle.THIN);subGroupStyle.setBorderBottom(BorderStyle.THIN);subGroupStyle.setBorderLeft(BorderStyle.THIN);subGroupStyle.setBorderRight(BorderStyle.THIN);subGroupStyle.setAlignment(HorizontalAlignment.CENTER);subGroupStyle.setVerticalAlignment(VerticalAlignment.CENTER);subGroupStyle.setFont(fontBold);return subGroupStyle;}/*** Create and return default data cell style** @param workbook Workbook* @return CellStyle*/private CellStyle createDefaultDataCellStyle(Workbook workbook) {// Create and return default data cell styleCellStyle dataCellStyle = workbook.createCellStyle();Font fontBold = workbook.createFont();fontBold.setBold(true);fontBold.setFontHeightInPoints((short) 11);dataCellStyle.setBorderTop(BorderStyle.THIN);dataCellStyle.setBorderBottom(BorderStyle.THIN);dataCellStyle.setBorderLeft(BorderStyle.THIN);dataCellStyle.setBorderRight(BorderStyle.THIN);dataCellStyle.setFont(fontBold);return dataCellStyle;}
}
4. 测试
public class PoiExportUtilTest {@Testpublic void exportToExcel() {PoiExportUtil poiExportUtil = new PoiExportUtil();List<OrderVO> orderVOList = generateOrders();poiExportUtil.exportToExcel(orderVOList, "order.xlsx");}public List<OrderVO> generateOrders() {String[] COUNTRIES = {"China", "Japan", "Canada"};Random RANDOM = new Random();List<OrderVO> orders = new ArrayList<>();for (int i = 0; i < 30; i++) {String orderNo = "OrderNo" + (i + 1);String orderUser = "User" + (i + 1);String orderTime = "Time" + (i + 1);String orderAmount = RANDOM.nextInt(10000) + ".00";String orderDesc = "Desc" + (i + 1);String orderRemark = "Remark" + (i + 1);String orderPhone = "Phone" + (i + 1);String orderZipCode = "ZipCode" + (i + 1);String orderCountry = COUNTRIES[RANDOM.nextInt(COUNTRIES.length)];String orderProvince = "Province" + (i + 1) % 3;String orderCity = "City" + (i + 1);String orderAddressDetail = "AddressDetail" + (i + 1);OrderVO order = createOrder(orderNo, orderUser, orderTime, orderAmount, orderDesc, orderRemark, orderPhone,orderZipCode, orderCountry, orderProvince, orderCity, orderAddressDetail);orders.add(order);}// Sort by orderCountry and orderTimereturn orders.stream().sorted(Comparator.comparing(OrderVO::getOrderCountry).thenComparing(OrderVO::getOrderProvince).thenComparing(OrderVO::getOrderTime)).collect(Collectors.toList());}private OrderVO createOrder(String orderNo, String orderUser, String orderTime, String orderAmount,String orderDesc, String orderRemark, String orderPhone, String orderZipCode,String orderCountry, String orderProvince, String orderCity, String orderAddressDetail) {OrderVO order = new OrderVO();order.setOrderNo(orderNo);order.setOrderUser(orderUser);order.setOrderTime(orderTime);order.setOrderAmount(orderAmount);order.setOrderDesc(orderDesc);order.setOrderRemark(orderRemark);order.setOrderPhone(orderPhone);order.setOrderZipCode(orderZipCode);order.setOrderCountry(orderCountry);order.setOrderProvince(orderProvince);order.setOrderCity(orderCity);order.setOrderAddressDetail(orderAddressDetail);return order;}
}
参考源代码
相关文章:
自定义注解实现Excel 导出
概述 一个用自定义注解实现导出字段定义的工具实现。 1. 注解定义,定义导出Excel的字段 Target(ElementType.FIELD) Retention(RetentionPolicy.RUNTIME) public interface PoiExportField {// Label of the columnString label();// Order of the column,default 0,means t…...
先求生存,再谋发展:俞敏洪的创业哲学与产品创新之路
引言: 在创业的道路上,每一个创业者都面临着无数的挑战和选择。俞敏洪,新东方教育科技集团的创始人,以其独特的创业哲学和坚韧不拔的精神,带领新东方从一个小小的培训机构成长为全球知名的教育品牌。他的成功经验告诉…...
【Spark】直接从DataFrame的schema创建表
// 基于DataFrame创建表 def createTable(dataFrame: DataFrame,partitionColumns: Array[String],databaseName: String,tableName: String): Unit = {...
Decimal要从str转换以避免精度问题
最近遇到一个python的小数的问题,本来应该很简单的小于判断,无论如何都不正确,而且浮点小数都没问题,但decimal小数有问题,给我整蒙了,后来才发现是对decimal不了解所致,如果你还用float转decim…...
STM32项目分享:智能家居安防系统
目录 一、前言 二、项目简介 1.功能详解 2.主要器件 三、原理图设计 四、PCB硬件设计 1.PCB图 2.PCB板及元器件图 五、程序设计 六、实验效果 七、资料内容 项目分享 一、前言 项目成品图片: 哔哩哔哩视频链接: https://www.bilibili.c…...
qt c++类继承QWidget和不继承有什么区别
class CheckBoxSetting {Q_OBJECT public:CheckBoxSetting(); };和 class CheckBoxSettingsEditor : public QWidget {Q_OBJECTpublic:explicit CheckBoxSettingsEditor(QWidget *parent 0);~CheckBoxSettingsEditor();有什么区别? 这两个类 CheckBoxSetting 和 C…...
什么是SIEM
SIEM 解决方案是一种企业级应用程序,可集中和自动化与网络安全相关的操作,该工具通过收集、分析和关联从组织 IT 基础设施中的各种实体聚合的网络事件来帮助应对网络威胁。 与帮助监控和评估组织物理空间中的危险的监视控制台相比,SIEM解决方…...
浅谈一下实例化
实例化对象是面向对象编程中非常重要的概念,它允许我们根据类的定义创建具体的对象,并操作这些对象的属性和方法。下面具体谈一下实例化对象的一些特点和用途: 封装性和复用性:实例化对象可以将数据和行为封装在一起,从…...
【人工智能】第三部分:ChatGPT的应用场景和挑战
人不走空 🌈个人主页:人不走空 💖系列专栏:算法专题 ⏰诗词歌赋:斯是陋室,惟吾德馨 目录 🌈个人主页:人不走空 💖系列专栏:算法专题 ⏰诗词歌…...
FLV 文件格式
FLV 总体结构 FLV 文件由 FLV文件头(FLV Header)和 FLV文件体(FLV Body)组成。 FLV 文件体由若干级联的 FLV标签(FLV Tag)组成。标签使用一个 PreviousTagSize(uint32_t)来保存前一个 FLV 标签的大小,第一个 PreviousTagSize 值为0。 一个 FLV 文件中的所有数据,如 视频…...
FENDI CLUB精酿啤酒品鉴体验
当提及“品质卓越,口感非凡”的啤酒时,FENDI CLUB精酿啤酒无疑是一个值得一试的选择。这款啤酒以其独特的酿造工艺和优质的原料,为消费者带来了与众不同的味觉享受。 一、独特的酿造工艺 FENDI CLUB精酿啤酒在酿造过程中,严格遵循…...
前端 CSS 经典:水波进度样式
前言:简单实现水波进度样式,简单好看。 效果图: 代码实现: <!DOCTYPE html> <html lang"en"><head><meta charset"utf-8" /><meta http-equiv"X-UA-Compatible" cont…...
深入解析CSS中的块级元素
块级元素在CSS中是一种常见的元素类型,具有一些特定的表现和行为特征。了解块级元素的定义和特点对于掌握CSS布局和样式设计至关重要。本文将从多个角度深入解析CSS中的块级元素,探讨其含义、特点以及在页面布局中的应用。 什么是块级元素? …...
PDF裁剪网站
裁剪 PDF – 修剪 PDF 文件中不需要的空白...
数据结构复习指导之外部排序
目录 外部排序 复习提示 1.外部排序的基本概念 2.外部排序的方法 2.1对大文件排序时使用的排序算法(2016) 3.多路平衡归并与败者树 4.置换-选择排序(生成初始归并段) 4.1置换-选择排序生成初始归并段的实例(2023) 5.最佳…...
【Python报错】已解决TypeError: can only concatenate str (not “int“) to str
解决Python报错:TypeError: can only concatenate str (not “int”) to str 在Python中,字符串连接是常见的操作,但如果你尝试将整数(int)与字符串(str)直接连接,会遇到TypeError: …...
Log4j日志级别介绍
Log4j 是一个广泛使用的 Java 日志记录框架,提供了多种日志级别,用于控制日志输出的详细程度。每个日志级别代表一种特定的重要性和紧急程度。 以下是 Log4j 的常见日志级别及其解读: FATAL(致命) 解释:表…...
[MQTT]服务器EMQX搭建SSL/TLS连接过程(wss://)
👉原文阅读 💡章前提示 本文采用8084端口进行连接,是EMQX 默认提供了四个常用的监听器之一,如果需要添加其他类型的监听器,可参考官方文档🔗管理 | EMQX 文档。 本文使用自签名CA,需要提前在L…...
【纯血鸿蒙】——响应式布局如何实现?
前面介绍了自适应布局,但是将窗口尺寸变化较大时,仅仅依靠自适应布局可能出现图片异常放大或页面内容稀疏、留白过多等问题。此时就需要借助响应式布局能力调整页面结构。 响应式布局 响应式布局是指页面内的元素可以根据特定的特征(如窗口…...
深入理解Django Serializer及其在Go语言中的实现20240604
深入理解Django Serializer及其在Go语言中的实现 在现代Web开发中,前后端分离已成为主流架构模式。作为开发者,我们经常需要处理数据的序列化和反序列化,以便在前后端之间传递数据。在Django中,Serializer是一个强大的工具&#…...
从WWDC看苹果产品发展的规律
WWDC 是苹果公司一年一度面向全球开发者的盛会,其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具,对过去十年 WWDC 主题演讲内容进行了系统化分析,形成了这份…...
【Redis技术进阶之路】「原理分析系列开篇」分析客户端和服务端网络诵信交互实现(服务端执行命令请求的过程 - 初始化服务器)
服务端执行命令请求的过程 【专栏简介】【技术大纲】【专栏目标】【目标人群】1. Redis爱好者与社区成员2. 后端开发和系统架构师3. 计算机专业的本科生及研究生 初始化服务器1. 初始化服务器状态结构初始化RedisServer变量 2. 加载相关系统配置和用户配置参数定制化配置参数案…...
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
MMaDA: Multimodal Large Diffusion Language Models
CODE : https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA,它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构…...
微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...
MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
什么是EULA和DPA
文章目录 EULA(End User License Agreement)DPA(Data Protection Agreement)一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA(End User License Agreement) 定义: EULA即…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...
