easypoi list中的map导出_如何优雅的导出 Excel
前言
公司項(xiàng)目最近有一個(gè)需要:報(bào)表導(dǎo)出。整個(gè)系統(tǒng)下來,起碼超過一百張報(bào)表需要導(dǎo)出。這個(gè)時(shí)候如何優(yōu)雅的實(shí)現(xiàn)報(bào)表導(dǎo)出,釋放生產(chǎn)力就顯得很重要了。下面主要給大家分享一下該工具類的使用方法與實(shí)現(xiàn)思路。
實(shí)現(xiàn)的功能點(diǎn)
對于每個(gè)報(bào)表都相同的操作,我們很自然的會(huì)抽離出來,這個(gè)很簡單。而最重要的是:如何把那些每個(gè)報(bào)表不相同的操作進(jìn)行良好的封裝,盡可能的提高復(fù)用性;針對以上的原則,主要實(shí)現(xiàn)了一下關(guān)鍵功能點(diǎn):
- 導(dǎo)出任意類型的數(shù)據(jù)
- 自由設(shè)置表頭
- 自由設(shè)置字段的導(dǎo)出格式
使用實(shí)例
上面說到了本工具類實(shí)現(xiàn)了三個(gè)功能點(diǎn),自然在使用的時(shí)候設(shè)置好這三個(gè)要點(diǎn)即可:
- 設(shè)置數(shù)據(jù)列表
- 設(shè)置表頭
- 設(shè)置字段格式
下面的export函數(shù)可以直接向客戶端返回一個(gè)excel數(shù)據(jù),其中productInfoPos為待導(dǎo)出的數(shù)據(jù)列表,ExcelHeaderInfo用來保存表頭信息,包括表頭名稱,表頭的首列,尾列,首行,尾行。因?yàn)槟J(rèn)導(dǎo)出的數(shù)據(jù)格式都是字符串型,所以還需要一個(gè)Map參數(shù)用來指定某個(gè)字段的格式化類型(例如數(shù)字類型,小數(shù)類型、日期類型)。這里大家知道個(gè)大概怎么使用就好了,下面會(huì)對這些參數(shù)進(jìn)行詳細(xì)解釋。
實(shí)現(xiàn)效果
源碼分析
哈哈,自己分析自己的代碼,有點(diǎn)意思。由于不方便貼出太多的代碼,大家可以先到github上clone源碼,再回來閱讀文章。?源碼地址?LZ使用的poi 4.0.1版本的這個(gè)工具,想要實(shí)用海量數(shù)據(jù)的導(dǎo)出自然得使用SXSSFWorkbook這個(gè)組件。關(guān)于poi的具體用法在這里我就不多說了,這里主要是給大家講解如何對poi進(jìn)行封裝使用。
成員變量
我們重點(diǎn)看ExcelUtils這個(gè)類,這個(gè)類是實(shí)現(xiàn)導(dǎo)出的核心,先來看一下三個(gè)成員變量。
private List list; private List excelHeaderInfos; private Map formatInfo;list
該成員變量用來保存待導(dǎo)出的數(shù)據(jù)。
ExcelHeaderInfo
該成員變量主要用來保存表頭信息,因?yàn)槲覀冃枰x多個(gè)表頭信息,所以需要使用一個(gè)列表來保存,ExcelHeaderInfo構(gòu)造函數(shù)如下ExcelHeaderInfo(int firstRow, int lastRow, int firstCol, int lastCol, String title)
- firstRow:該表頭所占位置的首行
- lastRow:該表頭所占位置的尾行
- firstCol:該表頭所占位置的首列
- lastCol:該表頭所占位置的尾行
- title:該表頭的名稱
ExcelFormat
該參數(shù)主要用來格式化字段,我們需要預(yù)先約定好轉(zhuǎn)換成那種格式,不能隨用戶自己定。所以我們定義了一個(gè)枚舉類型的變量,該枚舉類只有一個(gè)字符串類型成員變量,用來保存想要轉(zhuǎn)換的格式,例如FORMAT_INTEGER就是轉(zhuǎn)換成整型。因?yàn)槲覀冃枰邮芏鄠€(gè)字段的轉(zhuǎn)換格式,所以定義了一個(gè)Map類型來接收,該參數(shù)可以省略(默認(rèn)格式為字符串)。
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. 創(chuàng)建表頭
該方法用來初始化表頭,而創(chuàng)建表頭最關(guān)鍵的就是poi中Sheet類的addMergedRegion(CellRangeAddress var1)方法,該方法用于單元格融合。我們會(huì)遍歷ExcelHeaderInfo列表,按照每個(gè)ExcelHeaderInfo的坐標(biāo)信息進(jìn)行單元格融合,然后在融合之后的每個(gè)單元首行和首列的位置創(chuàng)建單元格,然后為單元格賦值即可,通過上面的步驟就完成了任意類型的表頭設(shè)置。2. 轉(zhuǎn)換數(shù)據(jù)
在進(jìn)行正文賦值之前,我們先要對原始數(shù)據(jù)列表轉(zhuǎn)換成字符串的二維數(shù)組,之所以轉(zhuǎn)成字符串格式是因?yàn)榭梢越y(tǒng)一的處理各種類型,之后有需要我們再轉(zhuǎn)換回來即可。
這個(gè)方法中我們通過使用反射技術(shù),很巧妙的實(shí)現(xiàn)了任意類型的數(shù)據(jù)導(dǎo)出(這里的任意類型指的是任意的報(bào)表類型,不同的報(bào)表,導(dǎo)出的數(shù)據(jù)肯定是不一樣的,那么在Java實(shí)現(xiàn)中的實(shí)體類肯定也是不一樣的)。要想將一個(gè)List轉(zhuǎn)換成相應(yīng)的二維數(shù)組,我們得知道如下的信息:
- 二維數(shù)組的列數(shù)
- 二維數(shù)組的行數(shù)
- 二維數(shù)組每個(gè)元素的值
如果獲取以上三個(gè)信息呢?
- 通過反射中的Field[] getDeclaredFields()這個(gè)方法獲取實(shí)體類的所有字段,從而間接知道一共有多少列
- List的大小不就是二維數(shù)組的行數(shù)了嘛
- 雖然每個(gè)實(shí)體類的字段名不一樣,那么我們就真的無法獲取到實(shí)體類某個(gè)字段的值了嗎?不是的,你要知道,你擁有了反射,你就相當(dāng)于擁有了全世界,那還有什么做不到的呢。這里我們沒有直接使用反射,而是使用了一個(gè)叫做BeanUtils的工具,該工具可以很方便的幫助我們對一個(gè)實(shí)體類進(jìn)行字段的賦值與字段值的獲取。很簡單,通過BeanUtils.getProperty(list.get(i), columnNames.get(j))這一行代碼,我們就獲取了實(shí)體list.get(i)中名稱為columnNames.get(j)這個(gè)字段的值。list.get(i)當(dāng)然是我們遍歷原始數(shù)據(jù)的實(shí)體類,而columnNames列表則是一個(gè)實(shí)體類所有字段名的數(shù)組,也是通過反射的方法獲取到的,具體實(shí)現(xiàn)可以參考LZ的源代碼。
3. 賦值正文
這里的正文指定是正式的表格數(shù)據(jù)內(nèi)容,其實(shí)這一些沒有太多的奇淫技巧,主要的功能在上面已經(jīng)實(shí)現(xiàn)了,這里主要是進(jìn)行單元格的賦值與導(dǎo)出格式的處理(主要是為了導(dǎo)出excel后可以進(jìn)行方便的運(yùn)算)。
導(dǎo)出工具類的核心方法就差不多說完了,下面說一下關(guān)于多線程查詢的問題。
多扯兩點(diǎn)
1. 多線程查詢數(shù)據(jù)
理想很豐滿,現(xiàn)實(shí)還是有點(diǎn)骨感的。LZ雖然對50w的數(shù)據(jù)分別創(chuàng)建20個(gè)線程去查詢,但是總體的效率并不是50w/20,而是僅僅快了幾秒鐘,知道原因的小伙伴可以給我留個(gè)言一起探討一下。
下面先說說具體思路:因?yàn)槎鄠€(gè)線程之間是同時(shí)執(zhí)行的,你不能夠保證哪個(gè)線程先執(zhí)行完畢,但是我們卻得保證數(shù)據(jù)順序的一致性。在這里我們使用了Callable接口,通過實(shí)現(xiàn)Callable接口的線程可以擁有返回值,我們獲取到所有子線程的查詢結(jié)果,然后合并到一個(gè)結(jié)果集中即可。那么如何保證合并的順序呢?我們先創(chuàng)建了一個(gè)FutureTask類型的List,該FutureTask的類型就是返回的結(jié)果集。
List>> tasks = new ArrayList<>();當(dāng)我們每啟動(dòng)一個(gè)線程的時(shí)候,就將該線程的FutureTask添加到tasks列表中,這樣tasks列表中的元素順序就是我們啟動(dòng)線程的順序。
FutureTask> task = new FutureTask<>(new listThread(map)); log.info("開始查詢第{}條開始的{}條記錄總結(jié)
以上是生活随笔為你收集整理的easypoi list中的map导出_如何优雅的导出 Excel的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: iOS里面MVC模式详解
- 下一篇: 二维数据的白化处理