自定义注解实现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是一个强大的工具&#…...
电子纸在日化行业的全新应用
电子纸在日化行业的全新应用 项目背景 在一日化龙头企业他们的洗衣粉产线在AGV小车取料到运输到产品包装工序时,因为取料粉车无明显区分标识,但是产品系列有十大类。在未采用晨控电子纸之前现场采用一个转盘分为十个区域,取料工序上方会有一…...
【Redis】Redis的双写问题
在分布式系统中,双写问题通常是指数据在多个存储系统(例如数据库和缓存)中更新时出现的不一致性。这种问题在使用 Redis 作为缓存层时尤为常见。具体来说,当数据在数据库和 Redis 缓存中存在副本时,任何对数据的更新操…...
生气时,你的“心”会发生什么变化?孟德尔随机化分析猛如虎,结果都是套路...
“不生气不生气,气出病来无人替”,不少人遇事常这样宽慰自己。事实上,“气死”真不是危言耸听。越来越多的研究证明了情绪稳定对健康的重要性,那么,当情绪频繁波动时,我们的心血管究竟会发生什么变化&#…...
页面加载性能分析时,有哪些常见的性能瓶颈需要特别注意?
在进行页面加载性能分析时,以下是一些常见的性能瓶颈,需要特别注意: 长页面加载时间: 页面加载时间超过行业标准或用户期望,导致用户流失。 高 CPU 使用率: 某些脚本或操作导致 CPU 使用率飙升,…...
Scanner
Java 有一个 Scanner 类,用这个类可以接受键盘输入。 步骤: 导入该类所在的包(要使用一个类的话就必须先导入该类所在的包)创建该类的对象调用里面的功能 Scanner 有两套系统。 第一套系统: nextInt(); nextDoubl…...
vue3实现录音与录像上传功能
录音 <script setup lang"ts"> import { onMounted, reactive, ref } from vue; import useInject from /utils/useInject;const props: any defineProps<{params?: any; }>();const recObj: any reactive({blob: null, });const { $global, $fn } …...
PHP小方法
一、随机生成姓名 二、随机获取身份证 三、随机获取手机号 四、随机获取省 五、通过身份证获取生日和性别 六、通过身份证获取年龄 七、获取访问IP 八、获取访问URL地址 九、陆续增加 //一、随机生成姓名 function generateName(){$arrXing getXingList();$numbXing …...
gulimall-search P125 springboot整合elasticsearch版本冲突
一、问题 spring-boot.version 2.2.4.RELEASE,在gulimall-search pom.xml中添加elasticsearch.version 7.4.2后,发现出现如下问题:elasticsearch版本是springboot引入的6.8.6,没有变为7.4.2。 二、原因 在gulimall-search 的pom文件中&#…...
如何在Coze中实现Bot对工作流的精准调用(如何提高Coze工作流调用的准确性和成功率)
文章目录 📖 介绍 📖🏡 演示环境 🏡📒 工作流(workflow)📒📝 创建设计工作流📝 添加工作流📝 调用工作流⚓️ 相关链接 ⚓️📖 介绍 📖 在使用Coze平台创建智能Bot时,您可能会遇到一个常见问题:即便添加了正确的工作流,Bot却没有按照预期调用它们。…...
毫米波雷达阵列天线设计综合1(MATLAB仿真)
1 天线设计目标 毫米波雷达探测目标的距离、速度和角度,其中距离和角度和天线设计相关性较强。天线增益越高,则根据雷达方程可知探测距离越远;天线波束越窄,则角度分辨率越高;天线副瓣/旁瓣越低,则干扰越少…...
wordpress 获取用户密码/推广衣服的软文
直接赋值 可能创建一个或者不创建对象,如果”aaa”这个字符串在java String池里不存在,会在java String池里创建一个创建一个String对象(“aaa”)。 然后str1指向这个内存地址,无论以后用这种方式创建多少个值为”aaa”的字符串对象&#x…...
phpcms做的网站/网络营销论文毕业论文
入口 A(fzu 1894) 普通的单调队列,trick是进队判断的符号选取(>wa , >ac). B(poj 2823) 没什么好说的 ,坑爹poj g,tle ;c,ac. C(hdu 3415) 尝试封装了一下单调队列。。。感觉也没有方便多少. 1 #define maxn 1000102 #define INF 10000000003 int a[maxn<<1],…...
企业建网站报价/百度推广关键词技巧定价
中关村在线消息:华为今日发布了2019年上半年业绩:上半年销售收入4013亿元,同比增长23.2%。其中消费者业务2208亿元,占比55%。华为今年上半年智能手机发货量(含荣耀)达到1.18亿台,同比增长24%。华为董事长梁华表示&…...
网站建设绩效考核方案ppt/电脑培训班附近有吗
我很想知道westwood算法的深意,不仅仅是算法本身,还包括它的名字。 是的,为什么叫westwood?有朋友告诉我这是一个地名,并建议我去美国西海岸转一圈,然后我就发现IT圈子里不明所以看起来高大上的名字都是地…...
潍坊网站建设兼职/上海好的seo公司
java 并发与线程池 java并发包使用Executor框架来进行线程的管理,Executor将任务的提交与执行过程分开,直接使用Runnable表示任务。future获取返回值。ExecutorService 继承了Executor接口,提供生命周器的管理,包括运行࿰…...
找个男做那个视频网站好/济南百度推广公司电话
首先是 注册码问题 推荐使用 zoho 邮箱,这个是免费的企业邮箱,重要的是不用填那些乱七八糟的东西,注册很方便 其二是 安装问题 安装时不要选中 安装后启动和初始化 类似的英文 ,后面可能会卡住,看终端你可以发现写着…...