由于大象以前做的報(bào)表距離現(xiàn)在時(shí)間已經(jīng)很久了,有些相關(guān)的業(yè)務(wù)數(shù)據(jù)已經(jīng)找不到,再加上這些數(shù)據(jù)涉及商業(yè)機(jī)密也不便公開(kāi),所以本篇主要著重于闡述解決問(wèn)題的思路,而不是去關(guān)注代碼實(shí)現(xiàn),當(dāng)然代碼肯定會(huì)有,我主要想告訴大家怎么去解決這類問(wèn)題。同時(shí)大象將在第二篇文章中,把自己遇到過(guò)的一些比較復(fù)雜的報(bào)表案例拿出來(lái)進(jìn)行分析,用抽絲剝繭的方式,讓報(bào)表開(kāi)發(fā)變得不再那么讓人無(wú)處下手,最多只是麻煩點(diǎn)而已。^_^
目前Java開(kāi)源報(bào)表比較有代表性的有JasperReports、Cewolf、iReport、JfreeChart等等技術(shù),它們大都是用來(lái)生成不同格式或圖形化的報(bào)表。而本篇文章要講的jxl,則是Java Excel API,它是一個(gè)成熟開(kāi)源的Excel電子表格讀取,修改,寫入的項(xiàng)目。我們可以利用它簡(jiǎn)單,便利的API生成我們想要的Excel數(shù)據(jù)報(bào)表。
除了jxl外,還有一個(gè)apache的poi開(kāi)源項(xiàng)目,它的功能非常強(qiáng)大,不僅可以操作Excel,還可以支持Word、PowerPoint等等格式,3.5之后的版本,還加入了對(duì)Excel 2007以后版本的支持。
當(dāng)然jxl也有不足的地方,最主要體現(xiàn)在,它支持生成的報(bào)表最大行數(shù)為65535,如果超過(guò)這個(gè)數(shù)量,將會(huì)產(chǎn)生異常,而且也不支持Excel 2007+版本。如果平時(shí)報(bào)表數(shù)據(jù)量不是很大的話,而且只是用來(lái)生成Excel格式的文件,大象建議還是用jxl比較好,它使用簡(jiǎn)單,性能也還不錯(cuò)。
根據(jù)大象的經(jīng)驗(yàn),定義一個(gè)報(bào)表模板,然后往里面填充數(shù)據(jù),是開(kāi)發(fā)復(fù)雜報(bào)表的一個(gè)比較不錯(cuò)的方法。大象將報(bào)表分為基本報(bào)表和復(fù)雜報(bào)表,所謂基本報(bào)表就是沒(méi)有復(fù)合表頭,每行一條數(shù)據(jù),并且提供的數(shù)據(jù)不需要查詢兩張以上的表。而復(fù)雜報(bào)表正好與之相反。不管是基本報(bào)表還是復(fù)雜報(bào)表,代碼有很多都是一樣的,不一樣的只是由業(yè)務(wù)復(fù)雜度產(chǎn)生的報(bào)表復(fù)雜度。
我們先來(lái)看看用jxl生成Excel報(bào)表的代碼模板:

上面這個(gè)是Cell(單元格)的通用格式化設(shè)置,WritableFont構(gòu)造函數(shù)的參數(shù)第1個(gè)是字體,第2個(gè)是字體大小,第3個(gè)是字體樣式,第4個(gè)false指的是正常顯示,默認(rèn)為italic傾斜顯示,第5個(gè)下劃線樣式,第6個(gè)指定字體顏色。設(shè)置完字體后,再設(shè)置單元格的格式,對(duì)于一般的單元格,不用設(shè)置背景色,除非報(bào)表顯示需要。金額及百分比的顯示要用到NumberFormat類,這里的格式指定為0.00而不是#.##是為了強(qiáng)制保留兩位小數(shù),對(duì)于這類數(shù)字一般在報(bào)表中都是設(shè)置為右對(duì)齊,這樣出來(lái)的報(bào)表比較好看。當(dāng)然最終還是要根據(jù)客戶的要求來(lái)決定樣式。

使用模板方式生成Excel除了已經(jīng)定義好表頭外,還有一個(gè)好處是可以預(yù)先定義固定列的寬度,這樣就免去了用代碼來(lái)設(shè)置列寬,如果是復(fù)雜表頭,也省去了不少設(shè)置表頭和合并單元格的代碼。請(qǐng)注意我前面用紅字加粗的固定列三個(gè)字,因?yàn)閳?bào)表的表頭不一定就是模板中設(shè)置的那樣,還有一種情況就是根據(jù)查詢條件的年和月,動(dòng)態(tài)生成表頭,這些表頭被排列在固定列的后面,報(bào)表生成后,這些列下面都有相應(yīng)的數(shù)據(jù)。關(guān)于這部分內(nèi)容我會(huì)在第二篇文章中介紹。上圖中,紅框的部分就是使用模板生成Excel的關(guān)鍵代碼,ResourceUtils是org.springframework.util包中的工具類。

上圖中的紅框部分與使用模板方式相比是不是點(diǎn)些細(xì)微的差別?請(qǐng)一定注意這些細(xì)節(jié)部分的不同。完全用jxl API創(chuàng)建Excel所有內(nèi)容都需要用代碼來(lái)實(shí)現(xiàn):

設(shè)置標(biāo)題及表頭并定義每列的寬度,setColumnView方法的第1個(gè)參數(shù)是列的索引,從0開(kāi)始,第2個(gè)參數(shù)是寬度,這個(gè)數(shù)值既不是像素也不是長(zhǎng)度,而是指字符數(shù),比如第1列的寬度可以理解為相當(dāng)于20個(gè)字符的寬度。以10pt的字體大小來(lái)算,實(shí)際上最多只能填充19個(gè)字符,而漢字最多10個(gè),其實(shí)主要還是需要通過(guò)測(cè)試來(lái)調(diào)整最終效果。
接下來(lái)就是填充數(shù)據(jù),這塊代碼不管是用模板方式還是API方式都是一樣的。

對(duì)于簡(jiǎn)單的單行表頭,填充數(shù)據(jù)很簡(jiǎn)單,將查詢結(jié)果填充到單元格中。前面定義了標(biāo)題和表頭,已經(jīng)占去了兩行,所以應(yīng)該從第3行開(kāi)始添加數(shù)據(jù),行的索引也是從0開(kāi)始,2表示第3行。如果是數(shù)字型的Cell需要用jxl.write.Number來(lái)進(jìn)行封裝,這樣生成的數(shù)據(jù)才不會(huì)因?yàn)榭赡苓^(guò)大或別的因素而被Excel自動(dòng)轉(zhuǎn)化成科學(xué)計(jì)數(shù)法。如果還是想用Lable來(lái)封裝數(shù)字,可以使用java.text. NumberFormat來(lái)做格式化。最后執(zhí)行關(guān)閉操作。
看到這里,有沒(méi)有感覺(jué)用jxl做報(bào)表非常簡(jiǎn)單?可以說(shuō)是,也可以說(shuō)不是。因?yàn)檫@里面最麻煩的地方在于取得業(yè)務(wù)數(shù)據(jù),另外對(duì)于復(fù)雜報(bào)表,for循環(huán)里面還會(huì)包含很多條件判斷、其它for循環(huán)以及合并行或列等操作。
業(yè)務(wù)數(shù)據(jù)的獲得有兩種方式,第1種是按報(bào)表的樣式建立一個(gè)對(duì)象模型,將要導(dǎo)出的數(shù)據(jù)全部填充到這個(gè)模型中,然后根據(jù)報(bào)表的格式添加數(shù)據(jù)。第2種是通過(guò)SQL語(yǔ)句,使用盡量少的查詢次數(shù)將所有結(jié)果查詢出來(lái),填充到數(shù)據(jù)模型中。不管是第1種還是第2種最典型的就是簡(jiǎn)單表頭,單表查詢,所有的數(shù)據(jù)都在Model對(duì)象里面,執(zhí)行一個(gè)循環(huán)操作就可以搞定了。但如果是復(fù)雜報(bào)表,第1種做法就不可取了,因?yàn)楫?dāng)你將這個(gè)數(shù)據(jù)對(duì)象的List實(shí)現(xiàn)后就會(huì)發(fā)現(xiàn),代碼之復(fù)雜,效率之低下(并不是指速度慢,而是指SQL查詢次數(shù)非常多)簡(jiǎn)直令人發(fā)指。而如果采取第2種做法,SQL語(yǔ)句無(wú)疑會(huì)很復(fù)雜(不考慮視圖),會(huì)有很多關(guān)聯(lián)查詢,如果作了分表分庫(kù)的設(shè)計(jì),那這個(gè)方式就徹底完蛋了。
既然這兩種都不是好的解決辦法,那么究竟還有沒(méi)有更好的解決方案呢?肯定是有的,就是分而治之,按照一種規(guī)則(比如時(shí)間),提取出與報(bào)表相關(guān)的最基礎(chǔ)數(shù)據(jù)至Table1中,然后對(duì)該Table1再進(jìn)行一次處理合并至Table2中,提取Table2至Table3,如果有必要還可以對(duì)Table3再處理一次到Table4,從Table1到Table4每個(gè)表里面都是一種報(bào)表數(shù)據(jù),如果按時(shí)間分組,假定Table1是按小時(shí),Table2按天,Table3按月,Table4按年,那么相應(yīng)的報(bào)表查詢就可以做到只對(duì)一張表進(jìn)行操作,而且報(bào)表需要的業(yè)務(wù)數(shù)據(jù)都已經(jīng)存在該表中。我所舉的這個(gè)方法是比業(yè)務(wù)較簡(jiǎn)單的情況,實(shí)際開(kāi)發(fā)中要根據(jù)當(dāng)前的業(yè)務(wù)來(lái)設(shè)計(jì),因?yàn)橛行╉?xiàng)目的業(yè)務(wù)確實(shí)是非常的復(fù)雜,并不像我所說(shuō)的這么簡(jiǎn)單。
大家一定不要過(guò)于迷信這種方式,世上是沒(méi)有完美解決方案的,也即所謂的沒(méi)有“銀彈”,這樣的處理方式對(duì)大部分報(bào)表還是適用的。但如果要是遇到所統(tǒng)計(jì)的業(yè)務(wù)基礎(chǔ)數(shù)據(jù)發(fā)生了變化,或者本身業(yè)務(wù)出現(xiàn)變更,甚至報(bào)表都發(fā)生變更,那么之前所獲得的這些數(shù)據(jù)就沒(méi)有什么意義了。因此,報(bào)表的實(shí)現(xiàn)方案一定要根據(jù)實(shí)現(xiàn)情況來(lái)分析,不要總想著有什么通用的解決辦法,一招鮮吃遍天這樣的想法是非常不可取的,軟件最不缺的就是變化,不要抗拒變化,因?yàn)槟愀緹o(wú)法阻止它,所以你得擁抱變化,享受變化。
本篇主要介紹了使用jxl生成報(bào)表的代碼模板以及獲取報(bào)表數(shù)據(jù)的一種處理方式,下一篇將通過(guò)兩個(gè)復(fù)雜報(bào)表案例的分析,來(lái)告訴大家如何進(jìn)行實(shí)現(xiàn)。
本文為菠蘿大象原創(chuàng),如要轉(zhuǎn)載請(qǐng)注明出處。http://www.aygfsteel.com/bolo