在實際項目中,特别是企業應用開發中,報表開發是其中很重要的一項功能,基本上都會要求将統計資料導出成Excel,不但如此,這些報表格式一般也比較複雜,尤其是顯示的資料,往往都是很多業務資料綜合而成的結果。大象根據自己以往做報表的經驗,進行了一下總結,給剛開始做報表開發或是有需要的朋友一點借鑒。由于大象以前做的報表距離現在時間已經很久了,有些相關的業務資料已經找不到,再加上這些資料涉及商業機密也不便公開,是以本篇主要着重于闡述解決問題的思路,而不是去關注代碼實作,當然代碼肯定會有,我主要想告訴大家怎麼去解決這類問題。同時大象将在第二篇文章中,把自己遇到過的一些比較複雜的報表案例拿出來進行分析,用抽絲剝繭的方式,讓報表開發變得不再那麼讓人無處下手,最多隻是麻煩點而已。^_^
目前Java開源報表比較有代表性的有JasperReports、Cewolf、iReport、JfreeChart等等技術,它們大都是用來生成不同格式或圖形化的報表。而本篇文章要講的jxl,則是Java Excel API,它是一個成熟開源的Excel電子表格讀取,修改,寫入的項目。我們可以利用它簡單,便利的API生成我們想要的Excel資料報表。除了jxl外,還有一個apache的poi開源項目,它的功能非常強大,不僅可以操作Excel,還可以支援Word、PowerPoint等等格式,3.5之後的版本,還加入了對Excel 2007以後版本的支援。
當然jxl也有不足的地方,最主要展現在,它支援生成的報表最大行數為65535,如果超過這個數量,将會産生異常,而且也不支援Excel 2007+版本。如果平時報表資料量不是很大的話,而且隻是用來生成Excel格式的檔案,大象建議還是用jxl比較好,它使用簡單,性能也還不錯。
根據大象的經驗,定義一個報表模闆,然後往裡面填充資料,是開發複雜報表的一個比較不錯的方法。大象将報表分為基本報表和複雜報表,所謂基本報表就是沒有複合表頭,每行一條資料,并且提供的資料不需要查詢兩張以上的表。而複雜報表正好與之相反。不管是基本報表還是複雜報表,代碼有很多都是一樣的,不一樣的隻是由業務複雜度産生的報表複雜度。
我們先來看看用jxl生成Excel報表的代碼模闆:
上面這個是Cell(單元格)的通用格式化設定,WritableFont構造函數的參數第1個是字型,第2個是字型大小,第3個是字型樣式,第4個false指的是正常顯示,預設為italic傾斜顯示,第5個下劃線樣式,第6個指定字型顔色。設定完字型後,再設定單元格的格式,對于一般的單元格,不用設定背景色,除非報表顯示需要。金額及百分比的顯示要用到NumberFormat類,這裡的格式指定為0.00而不是#.##是為了強制保留兩位小數,對于這類數字一般在報表中都是設定為右對齊,這樣出來的報表比較好看。當然最終還是要根據客戶的要求來決定樣式。
使用模闆方式生成Excel除了已經定義好表頭外,還有一個好處是可以預先定義固定列的寬度,這樣就免去了用代碼來設定列寬,如果是複雜表頭,也省去了不少設定表頭和合并單元格的代碼。請注意我前面用紅字加粗的固定列三個字,因為報表的表頭不一定就是模闆中設定的那樣,還有一種情況就是根據查詢條件的年和月,動态生成表頭,這些表頭被排列在固定列的後面,報表生成後,這些列下面都有相應的資料。關于這部分内容我會在第二篇文章中介紹。上圖中,紅框的部分就是使用模闆生成Excel的關鍵代碼,ResourceUtils是org.springframework.util包中的工具類。
上圖中的紅框部分與使用模闆方式相比是不是點些細微的差别?請一定注意這些細節部分的不同。完全用jxl API建立Excel所有内容都需要用代碼來實作:
設定标題及表頭并定義每列的寬度,setColumnView方法的第1個參數是列的索引,從0開始,第2個參數是寬度,這個數值既不是像素也不是長度,而是指字元數,比如第1列的寬度可以了解為相當于20個字元的寬度。以10pt的字型大小來算,實際上最多隻能填充19個字元,而漢字最多10個,其實主要還是需要通過測試來調整最終效果。
接下來就是填充資料,這塊代碼不管是用模闆方式還是API方式都是一樣的。
對于簡單的單行表頭,填充資料很簡單,将查詢結果填充到單元格中。前面定義了标題和表頭,已經占去了兩行,是以應該從第3行開始添加資料,行的索引也是從0開始,2表示第3行。如果是數字型的Cell需要用jxl.write.Number來進行封裝,這樣生成的資料才不會因為可能過大或别的因素而被Excel自動轉化成科學計數法。如果還是想用Lable來封裝數字,可以使用java.text. NumberFormat來做格式化。最後執行關閉操作。
看到這裡,有沒有感覺用jxl做報表非常簡單?可以說是,也可以說不是。因為這裡面最麻煩的地方在于取得業務資料,另外對于複雜報表,for循環裡面還會包含很多條件判斷、其它for循環以及合并行或列等操作。業務資料的獲得有兩種方式,第1種是按報表的樣式建立一個對象模型,将要導出的資料全部填充到這個模型中,然後根據報表的格式添加資料。第2種是通過SQL語句,使用盡量少的查詢次數将所有結果查詢出來,填充到資料模型中。不管是第1種還是第2種最典型的就是簡單表頭,單表查詢,所有的資料都在Model對象裡面,執行一個循環操作就可以搞定了。但如果是複雜報表,第1種做法就不可取了,因為當你将這個資料對象的List實作後就會發現,代碼之複雜,效率之低下(并不是指速度慢,而是指SQL查詢次數非常多)簡直令人發指。而如果采取第2種做法,SQL語句無疑會很複雜(不考慮視圖),會有很多關聯查詢,如果作了分表分庫的設計,那這個方式就徹底完蛋了。既然這兩種都不是好的解決辦法,那麼究竟還有沒有更好的解決方案呢?肯定是有的,就是分而治之,按照一種規則(比如時間),提取出與報表相關的最基礎資料至Table1中,然後對該Table1再進行一次處理合并至Table2中,提取Table2至Table3,如果有必要還可以對Table3再處理一次到Table4,從Table1到Table4每個表裡面都是一種報表資料,如果按時間分組,假定Table1是按小時,Table2按天,Table3按月,Table4按年,那麼相應的報表查詢就可以做到隻對一張表進行操作,而且報表需要的業務資料都已經存在該表中。我所舉的這個方法是比業務較簡單的情況,實際開發中要根據目前的業務來設計,因為有些項目的業務确實是非常的複雜,并不像我所說的這麼簡單。大家一定不要過于迷信這種方式,世上是沒有完美解決方案的,也即所謂的沒有“銀彈”,這樣的處理方式對大部分報表還是适用的。但如果要是遇到所統計的業務基礎資料發生了變化,或者本身業務出現變更,甚至報表都發生變更,那麼之前所獲得的這些資料就沒有什麼意義了。是以,報表的實作方案一定要根據實作情況來分析,不要總想着有什麼通用的解決辦法,一招鮮吃遍天這樣的想法是非常不可取的,軟體最不缺的就是變化,不要抗拒變化,因為你根本無法阻止它,是以你得擁抱變化,享受變化。
本篇主要介紹了使用jxl生成報表的代碼模闆以及擷取報表資料的一種處理方式,下一篇将通過兩個複雜報表案例的分析,來告訴大家如何進行實作。本文為鳳梨大象原創,如要轉載請注明出處。
posted on 2014-01-07 16:12 鳳梨大象 閱讀(4719) 評論(2) 編輯 收藏 所屬分類: Java