easypoi list中的map导出_如何优雅的导出 Excel

作者:你在我家门口来源:https://juejin.im/post/5c6b6b126fb9a04a0c2f024f

前言

公司项目最近有一个需要:报表导出。整个系统下来,起码超过一百张报表需要导出。这个时候如何优雅的实现报表导出,释放生产力就显得很重要了。下面主要给大家分享一下该工具类的使用方法与实现思路。

实现的功能点

对于每个报表都相同的操作,我们很自然的会抽离出来,这个很简单。而最重要的是:如何把那些每个报表不相同的操作进行良好的封装,尽可能的提高复用性;针对以上的原则,主要实现了一下关键功能点:

  • 导出任意类型的数据
  • 自由设置表头
  • 自由设置字段的导出格式

使用实例

上面说到了本工具类实现了三个功能点,自然在使用的时候设置好这三个要点即可:

  • 设置数据列表
  • 设置表头
  • 设置字段格式

下面的export函数可以直接向客户端返回一个excel数据,其中productInfoPos为待导出的数据列表,ExcelHeaderInfo用来保存表头信息,包括表头名称,表头的首列,尾列,首行,尾行。因为默认导出的数据格式都是字符串型,所以还需要一个Map参数用来指定某个字段的格式化类型(例如数字类型,小数类型、日期类型)。这里大家知道个大概怎么使用就好了,下面会对这些参数进行详细解释。

实现效果

472358c2604a13960fb3969a11e5de4c.png

源码分析

哈哈,自己分析自己的代码,有点意思。由于不方便贴出太多的代码,大家可以先到github上clone源码,再回来阅读文章。✨源码地址✨LZ使用的poi 4.0.1版本的这个工具,想要实用海量数据的导出自然得使用SXSSFWorkbook这个组件。关于poi的具体用法在这里我就不多说了,这里主要是给大家讲解如何对poi进行封装使用。

成员变量

我们重点看ExcelUtils这个类,这个类是实现导出的核心,先来看一下三个成员变量。

 private List list; private List excelHeaderInfos; private Map formatInfo;

list

该成员变量用来保存待导出的数据。

ExcelHeaderInfo

该成员变量主要用来保存表头信息,因为我们需要定义多个表头信息,所以需要使用一个列表来保存,ExcelHeaderInfo构造函数如下ExcelHeaderInfo(int firstRow, int lastRow, int firstCol, int lastCol, String title)

  • firstRow:该表头所占位置的首行
  • lastRow:该表头所占位置的尾行
  • firstCol:该表头所占位置的首列
  • lastCol:该表头所占位置的尾行
  • title:该表头的名称

ExcelFormat

该参数主要用来格式化字段,我们需要预先约定好转换成那种格式,不能随用户自己定。所以我们定义了一个枚举类型的变量,该枚举类只有一个字符串类型成员变量,用来保存想要转换的格式,例如FORMAT_INTEGER就是转换成整型。因为我们需要接受多个字段的转换格式,所以定义了一个Map类型来接收,该参数可以省略(默认格式为字符串)。

public enum ExcelFormat { FORMAT_INTEGER("INTEGER"), FORMAT_DOUBLE("DOUBLE"), FORMAT_PERCENT("PERCENT"), FORMAT_DATE("DATE"); private String value; ExcelFormat(String value) { this.value = value; } public String getValue() { return value; }}

核心方法

1. 创建表头

该方法用来初始化表头,而创建表头最关键的就是poi中Sheet类的addMergedRegion(CellRangeAddress var1)方法,该方法用于单元格融合。我们会遍历ExcelHeaderInfo列表,按照每个ExcelHeaderInfo的坐标信息进行单元格融合,然后在融合之后的每个单元首行和首列的位置创建单元格,然后为单元格赋值即可,通过上面的步骤就完成了任意类型的表头设置。
ddcf045c5abc1f4b6310b9d0b79060c8.png

2. 转换数据

在进行正文赋值之前,我们先要对原始数据列表转换成字符串的二维数组,之所以转成字符串格式是因为可以统一的处理各种类型,之后有需要我们再转换回来即可。

e6b699de1c751a64a05bf4812aa08ff3.png

这个方法中我们通过使用反射技术,很巧妙的实现了任意类型的数据导出(这里的任意类型指的是任意的报表类型,不同的报表,导出的数据肯定是不一样的,那么在Java实现中的实体类肯定也是不一样的)。要想将一个List转换成相应的二维数组,我们得知道如下的信息:

  • 二维数组的列数
  • 二维数组的行数
  • 二维数组每个元素的值

如果获取以上三个信息呢?

  • 通过反射中的Field[] getDeclaredFields()这个方法获取实体类的所有字段,从而间接知道一共有多少列
  • List的大小不就是二维数组的行数了嘛
  • 虽然每个实体类的字段名不一样,那么我们就真的无法获取到实体类某个字段的值了吗?不是的,你要知道,你拥有了反射,你就相当于拥有了全世界,那还有什么做不到的呢。这里我们没有直接使用反射,而是使用了一个叫做BeanUtils的工具,该工具可以很方便的帮助我们对一个实体类进行字段的赋值与字段值的获取。很简单,通过BeanUtils.getProperty(list.get(i), columnNames.get(j))这一行代码,我们就获取了实体list.get(i)中名称为columnNames.get(j)这个字段的值。list.get(i)当然是我们遍历原始数据的实体类,而columnNames列表则是一个实体类所有字段名的数组,也是通过反射的方法获取到的,具体实现可以参考LZ的源代码。

3. 赋值正文

这里的正文指定是正式的表格数据内容,其实这一些没有太多的奇淫技巧,主要的功能在上面已经实现了,这里主要是进行单元格的赋值与导出格式的处理(主要是为了导出excel后可以进行方便的运算)。

febe5c68ec525ddbfdcafde6aec15f6b.png

导出工具类的核心方法就差不多说完了,下面说一下关于多线程查询的问题。

多扯两点

1. 多线程查询数据

理想很丰满,现实还是有点骨感的。LZ虽然对50w的数据分别创建20个线程去查询,但是总体的效率并不是50w/20,而是仅仅快了几秒钟,知道原因的小伙伴可以给我留个言一起探讨一下。

下面先说说具体思路:因为多个线程之间是同时执行的,你不能够保证哪个线程先执行完毕,但是我们却得保证数据顺序的一致性。在这里我们使用了Callable接口,通过实现Callable接口的线程可以拥有返回值,我们获取到所有子线程的查询结果,然后合并到一个结果集中即可。那么如何保证合并的顺序呢?我们先创建了一个FutureTask类型的List,该FutureTask的类型就是返回的结果集。

List>> tasks = new ArrayList<>();

当我们每启动一个线程的时候,就将该线程的FutureTask添加到tasks列表中,这样tasks列表中的元素顺序就是我们启动线程的顺序。

 FutureTask> task = new FutureTask<>(new listThread(map)); log.info("开始查询第{}条开始的{}条记录

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/566343.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c++ list容器获取第n给元素_Java总结之容器家族--Collection*

一、概述Collection是[收集品]的意思,这里称[容器],是java中的一个接口&#xff0c;位于java.util包下Collection下有三大接口&#xff1a;List(列表)、Set(集合)、Queue(队列)容器接口子类及方法二、List接口List&#xff1a;列表&#xff0c;顾名思义是一种表结构&#xff0c…

eclipemaven本地仓库依赖_只用一招,让你Maven依赖下载速度快如闪电

一、背景众所周知&#xff0c;Maven对于依赖的管理让我们程序员感觉爽的不要不要的&#xff0c;但是由于这货是国外出的&#xff0c;所以在我们从中央仓库下载依赖的时候&#xff0c;速度如蜗牛一般&#xff0c;让人不能忍&#xff0c;并且这也是大多数程序员都会遇到的问题。今…

和华为杯_2019全国大学生物联网设计竞赛(华为杯)拉开序幕

由教育部高等学校计算机类专业教学指导委员会主办&#xff0c;四川大学承办&#xff0c;华为协办&#xff0c;上海交通大学和机械工业出版社华章公司联合支持的2019年全国大学生物联网设计竞赛(华为杯)全国巡回技术讲座暨宣传活动5月9日在郑州大学拉开序幕。全国大学生物联网设…

flatform installer web 安装php_web安装平台-微软web服务器配置安装工具(Web Platform Installer)5.0 官方最新版-东坡下载...

这里为您提供的是微软官方的web服务器配置安装工具(Web Platform Installer),他可以帮助你快速的配置你所需要各种web环境。Web服务器又称为WWW服务器&#xff0c;它是放置一般网站的服务器。一台Web服务器上可以建立多个网站&#xff0c;各网站的拥有者只需要把做好的网页和相…

e.printstacktrace()为什么没有输出信息_不输入内容,能不能直接输出内容?

这段时间&#xff0c;我确信了一件事。想要输出内容&#xff0c;不需要“先输入”。直接输出&#xff0c;是能做到的。网络上流行一种观点&#xff1a;先输入&#xff0c;再输出&#xff01;我认为这是错的。人可以不输入知识&#xff0c;直接输出内容。人在世界上活了这些年。…

用python祝福父亲节_父亲节给爸爸的祝福语微信48条

父亲节给爸爸的祝福语微信48条撑起亲情的保护伞&#xff0c;风雨再大也不怕&#xff1b;摘下严厉的面具&#xff0c;再硬的心也柔软&#xff1b;望着沧桑的背影&#xff0c;感动常在心间。父爱如山&#xff0c;父亲是永远的靠山。父亲节快乐&#xff01;下面是小编为大家整理推…

mac python运行按哪个键_#mac python如何使用教程#怎么在mac终端运行python程序

mac如何投屏手机1.首先&#xff0c;解iPhone&#xff0c;然后用手指从下往上&#xff0c;打开控心&#xff0c;在其中就可以找到AirPlay了屏幕镜像。2.接来下就来教大家如何使用AirPlay吧&#xff0c;首先将手机和电脑连接在同一WiFi网络之下&#xff0c;这是投屏成功的前提条件…

informatica 许可_Informatica安装教程

11.打开\961_Server_Installer_win32-x86\Server\install.exe&#xff1b;22.选中 安装Informatica 9.6.1 点下一步&#xff1b;33.继续 下一步44. 选取许可证密钥文件&#xff0c;修改安装目录&#xff0c;点下一步&#xff1b;55. 点 “安装”&#xff0c;直到安装…

python读取微信收款_python 处理微信对账单数据的实例代码

下面一段代码给大家介绍python 处理微信对账单数据&#xff0c;具体代码如下所示&#xff1a;#下载对账单并存储到数据库app.route("/bill/",methods["GET","POST"])def download_bill(date):pay MyWeiXinPay()#自己的支付类bill pay.download_…

mouseup 左键_javascript中mouseup事件丢失的原因与解决办法

这篇文章主要跟大家介绍了关于Javascript中mouseup事件丢失的原因与解决办法的相关资料&#xff0c;文中给出详细的示例代码供大家参考学习&#xff0c;需要的朋友们下面跟着小编一起来学习学习吧。前言当实现类似Excel选中区域的功能时&#xff0c;经常出现 mouseup 事件丢失的…

sqlite数据库主键自增_sqlite 中主键id自增的方法(转)

困扰了一段时间&#xff0c;先前的解决方法是定义静态变量rid0&#xff0c;然后把rid插入数据库的id列中&#xff0c;但是每次重启的时候&#xff0c;rid就自动从0开始&#xff0c;就会导致有相同的id&#xff0c;插入数据库发生错误。今天在网上又查了一下&#xff0c;发现了以…

万兆网卡实际吞吐量_案例探索 | 千兆/万兆网卡每秒转发包数处理能力上限到底有多大?...

“侦破”网卡传输能力的“个”案 作者:李烨楠 一个平静的下午,在某监控大厅,应急召集令发出,一时间应急电话、汇报、询问声音响成一片。这是怎么了?原来某重要+系统应用交易严重超时,业务产生大量积压,无法顺利进行。 一、问题到底出在哪里? 系统架构简单明了:后台为O…

android shell检查是否锁屏_android打开关闭屏幕

打开和关闭手机屏幕方法&#xff1a;1.关闭屏幕//设备管理者private DevicePolicyManager mDevicePolicyManager;//关屏组件private ComponentName mCompName;onCreate(){mDevicePolicyManager (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);// 申请…

int mysql_「MYSQL」MYSQL中的int(11)到底代表什么意思?

一、前言在工作中经常要与mysql打交道&#xff0c;但是对mysql的各个字段类型一直都是一知半解&#xff0c;因此写本文总结记录一番。二、简介对于int类型的一些基础知识其实上图已经说的很明白了&#xff0c;在这里想讨论下常用的int(11)代表什么意思&#xff0c;很长时间以来…

mysqlfront连接MySQL错误_使用MySql-Front远程连接MySQL失败的解决办法

新安装的mysql默认是root用户&#xff0c;且密码为空&#xff0c;默认端口是3306。通过SSH可以连接并且登录&#xff0c;但是使用MySQL-Front等客户端连接失败。解决办法&#xff1a;1.确认Linux防火墙3306端口打开。在/etc/sysconfig/iptables文件中&#xff0c;添加一行&…

mysql数据库多少张表会影响性能_mysql数据库多少张表会影响性能

{"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],"search_count":[{"count_phone":4,"count":4}]},"card":[{"des":"阿里云数据库专家保驾护航&#xff0c;为用户…

mysql初级数据库_MYSQL初级数据库操作

MYSQL是个好东西啊&#xff0c;只是比较会折腾人&#xff0c;所以我先记录一下MySQL 数据备份的基础知识&#xff0c;省得以后又去东找西找Windows文件名、目录名不区分大小写&#xff0c;Linux区分大小写查看MySQL中的数据库这里有一个shop的数据库&#xff0c;information_sc…

odp 加固 mysql_creator是一款为php框架odp的脚手架工具

creator项目介绍creator是一款为php框架odp的脚手架工具&#xff0c;主要用于生成dao层&#xff0c;dataService层&#xff0c;pageService层,controller层,action层,避免重复性劳动和提高工作效率软件架构软件架构说明使用说明(ps:配置已支持当前项目组文件路径,放置在app同级…

java中的mod运算_Java中的运算符与控制语句

运算符与控制语句是Java语言中运用最为基础也是最为广泛的&#xff0c;所以今天就详细的介绍下运算符与控制语句。一、运算符1.算数运算符1).单目运算符&#xff1a; 、- 、 、-- A&#xff1a; 表示 a.正号&#xff08;一般省略&#xff09;&#xff1b;b.加法运算&#xff1b…

mysql如何查看表拥有的键_如何查看表或列的所有外键?

白板的微信表&#xff1a;SELECT TABLE_NAME,COLUMN_NAME,CONSTRAINT_NAME, REFERENCED_TABLE_NAME,REFERENCED_COLUMN_NAMEFROMINFORMATION_SCHEMA.KEY_COLUMN_USAGEWHEREREFERENCED_TABLE_SCHEMA ANDREFERENCED_TABLE_NAME TABLE_NAME,COLUMN_NAME,CONSTRAINT_NAME, REFER…