天天看點

利用複合的javabean構造基于jasperreports的子報表

  最近非常高興看到新版本的jasperreports和ireport釋出,雖然隻是增加了一些不實用的功能(編譯期錯誤處理還值得稱道),雖然感覺修正的bug不如新出現的bug多-_-!!,雖然新的版本一如既往的難以使用….但無論怎麼說,開源的jasperreports和開源的ireport仍然為我們的報表開發帶來了很多友善之處。 閑話少說^_^,這幾天公司開發的系統基本完工了,考慮到後期客戶需要,在統計和查詢子產品可能需要添加報表下載下傳功能,查詢還好辦,統計的話就涉及到報表的嵌套。比如我們在統計一個公司的業績時,需要在公司總表中列出各個部門,并對各個部門(不确定的部門個數)進行統計并彙總到公司總表中。甚至的,部門下面還可能有子部門…,提供報表下載下傳時,對部門的統計需要做成子表嵌到公司表中,而且,部門表統計的資料源是取自公司表資料源中的一部分….。 情況大體如上面所述,按照一般的報表設計思路,我們可能是先通過資料庫查詢公司總表,擷取公司的部門id号并把id号傳遞給部門子表,再分别讓部門子表去從資料庫擷取資料進行統計。這樣做我們必須在處理前把jdbc connection傳遞給jasperreports,讓jasperreports進行資料庫的讀取操作。而在我們的系統中,統計的資料其實已經在背景處理完并封裝到了一個複合的javabean中(複合bean:比如一個表單vo對象中還有其他的vo集合),之是以不使用jasperreports處理統計主要是考慮到了業務處理的需要。單一的.jasper很難根據多種業務需求進行變通,如果使用javabean,我們可以利用web伺服器的緩存避免重複的資料庫連接配接操作,而且在javabean中,我們實際上可以在下載下傳前對統計資料進行一定的變動,比如可以再次進行人工處理…. 前次,我曾經介紹過可以通過使用JRBeanCollectionDataSource()傳回一個JRDataSource,當時javabean 的資料類型都是原始類型,不曾碰到過使用集合類的複合javabean。這次考慮仍然使用javabean 來構造資料源,由于對ireport的datasource的處理機制不是很熟悉,經過很多次嘗試後才摸索出一個往子報表插入特定資料源的辦法(不是傳遞父報表的資料源,而是将父報表的一個變量當成資料源傳遞給子報表!) DEMO: 準備工作: 一、程式準備: 1、 建立複合javabean :MainVO.java:(getter和setter方法自寫)

package com.test; import java.util.List; public class MainVO {     private String title ;     private String time ;     private List<SubVO> subList ; }  

  2、 建立子表javabean: SubVO.java(getter和setter方法自寫)

package com.test; public class SubVO {   private String name ;

  3、 建立JRAbstractBeanDataSourceProvider:TestSubReport.java  

package com.test; public class TestSubReport extends JRAbstractBeanDataSourceProvider {     public TestSubReport() {        super (MainVO. class );     }     public JRDataSource create(JasperReport arg0) throws JRException {               List<MainVO> mainList = new ArrayList<MainVO>();

        List<SubVO> list = new ArrayList<SubVO>();        MainVO vo;        

        ……        return new JRBeanCollectionDataSource(mainList);     }

  4、 建立外部測試類:TestMain.java:

public static void main(String[] args) {  String filename = "bin/SubReport.jasper" ;  String outFileName = "bin/Out.html" ;     try { JasperPrint print = JasperFillManager.fillReport(filename, new  HashMap(), new JRBeanCollectionDataSource(mainList));      JRExporter exporter = new JRHtmlExporter();      exporter.setParameter(JRExporterParameter. OUTPUT_FILE_NAME ,         outFileName);      exporter.setParameter(JRExporterParameter. JASPER_PRINT , print);      exporter.exportReport();        } catch (JRException e) {            e.printStackTrace();        }     }

  5、 建立Scriptlet:reportScriptlet.java 注意: 如果你從父報表傳給子報表的資料源是個集合類型,且不需要任何的資料處理,這步可以省略,但需要注意我在第二部分的第7步驟的提示。  該類是用來進行類型轉換的,要想讓jasperreports識别資料源就必須把集合類封裝到JRDataSource中。由于父報表把資料源傳遞給子報表是在afterDetailEval()方法之後,是以隻需要重寫該方法:

package com.test; public class reportScriptlet extends JRAbstractScriptlet {         @Override     public void afterDetailEval() throws JRScriptletException {        System. out .println( "afterDetailEval..." );        List subList = (List)getFieldValue( "subList" );        JRDataSource jr = new JRBeanCollectionDataSource(subList);                 setVariableValue( "other" , jr);     } }

     該Scriptlet供父報表使用   二、報表準備        由于ireport的漢化很不完整,這裡就使用英文的界面做demo(有興趣漢化的,可以編輯位于ireport.jar包的it.businesslogic.ireport.locale下的Ireport_zh_CN.properties)。 1、 建立父報表:SubReport.jrxml,通過菜單欄:Data -->Connections /Datasources -->new選擇JRDataSourceProvider定義如圖:

利用複合的javabean構造基于jasperreports的子報表

                       Test 成功後(前提是先在ireport的classpath中設定工程編譯檔案夾路徑)save。   2、 設定另一個資料源(給子報表用)在這裡我選擇了使用Custom JRDataSource

利用複合的javabean構造基于jasperreports的子報表

                      使用Custom JRDataSource這裡我必須在項目中編寫一個額外的類用于測試:          

package com.test; public class CRDSFactory{     public static JRDataSource createDatasource(){     List<SubVO> list = new ArrayList<SubVO> ();             ……     return new JRBeanCollectionDataSource(list);     } }

                     Test 成功後 save。   3、 注冊字段Fileld 利用菜單中的Data  --> Report Query -->DataSource Provider 擷取字段,然後全選擷取到的字段點選确認注冊。

利用複合的javabean構造基于jasperreports的子報表

      4、 添加變量Variables 該變量用途是作為父報表傳遞給子報表的資料源,是以類型為JRDataSource                     如圖:

利用複合的javabean構造基于jasperreports的子報表

  5、 建立子報表: SubReport_subreport0.jrxml,(名字由系統生成) 點選工具欄中的”SubReport”圖示,并确定好子報表的位置,利用系統的wizard一步一步設定,注意在第2步設定”Connection/Datasource ”時最好選擇”no connection or datasource”,因為我們的datasource是父報表中的一個變量)   6、 将ireport的Files視窗的其他報表檔案關閉(大概需要這樣,前幾次因為沒關閉出了點問題,不清楚什麼原因)如果看不到Files視窗,可以通過菜單的View --> Docking panes -->Files 回顯。單獨選擇剛才為該子報表而設定的資料源“custds”,通過剛才的Report Query -->JavaBean Data Source讀取子報表相關的javabean屬性。選擇後點ok将其注冊到Fields中。如圖:  

利用複合的javabean構造基于jasperreports的子報表

  7、 打開父報表,在設計視窗的子報表上右鍵,選擇Properties -->SubReport,設定由父報表傳遞給子報表的資料源:          

利用複合的javabean構造基于jasperreports的子報表

                  提示:如果你略過了第一部分的第5步,這裡的“ $V{other} ”要改成“ new  net.sf.jasperreports.engine.data.JRBeanCollectionDataSource($V{other})”

8、 依次編譯(使用動态連接配接)子報表、父報表(注意資料源的對應關系),如果能通過父報表看到子報表的内容被填充,則說明測試成功了!如果不成功,檢查剛才設定是否有遺漏的地方,最好重新建立子報表,有時并不是我們設定問題,ireport目前還不是很穩定,在編譯和儲存資料時很容易出錯,有時連子報表都不認-_-!! 9、                        效果圖:(沒有修飾,确實很難看…)           

利用複合的javabean構造基于jasperreports的子報表

                    注意事項: ♦        熟悉jaspereports的以手動編輯代碼為主, ireport為輔,使用ireport時有時也必須手動編輯jasperreport,特别是在編譯出錯的時候。 ♦       不能把父報表的一個變量同時傳遞給多個子報表,不然可能隻能顯示一個或什麼都不顯示,如果需要這麼做,請定義多個變量。 ♦      子報表的添加不要用舊報表,即使你的舊報表是剛才使用的子報表。 ♦      如果子報表還要嵌套子報表的話,可以通過為子報表編寫一個Scriptlet實作。                                      不過要清楚的是子報表有可能不執行afterDetailEval()和beforeDetailEval()(比如使用jdbc連 接,這也許跟子報表的資料源選擇有關),最好先測試,具體原因希望達人告知! ♦     在使用ireport進行開發時,當修改了某個類時,就需要重新開機ireport才能看到修改的效果,即使是使用ireport的Scriptlet編輯器也一樣。 ♦     不是越高版本越好用,主要是缺少使用文檔,連javadoc資料都很貧乏,推薦1.3.0,1.3.0的已經有出document了,不過需要$才行,也不好買到。