由于大象以前做的報表距離現在時間已經很久了,有些相關的業務數據已經找不到,再加上這些數據涉及商業機密也不便公開,所以本篇主要著重于闡述解決問題的思路,而不是去關注代碼實現,當然代碼肯定會有,我主要想告訴大家怎么去解決這類問題。同時大象將在第二篇文章中,把自己遇到過的一些比較復雜的報表案例拿出來進行分析,用抽絲剝繭的方式,讓報表開發變得不再那么讓人無處下手,最多只是麻煩點而已。^_^
目前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生成報表的代碼模板以及獲取報表數據的一種處理方式,下一篇將通過兩個復雜報表案例的分析,來告訴大家如何進行實現。
本文為菠蘿大象原創,如要轉載請注明出處。http://www.aygfsteel.com/bolo