軟件介紹JasperReport是一款免費開源的報表生成工具,我們可以通過這款軟件來制作各種復雜的報表。而且JasperReport中文版還能夠將制作好的報表文件轉換成PDF,HTML,或者XML格式。這款軟件是JAVA設計人員經常會使用到的重要工具之一。 JasperReport軟件簡介JasperReport是一個強大、靈活的報表生成工具,能夠展示豐富的頁面內容,并將之轉換成PDF,HTML,或者XML格式。該庫完全由Java寫成,可以用于在各種Java應用程序,包括J2EE,Web應用程序中生成動態(tài)內容。 Jasperreport是開源的,這給我們帶來很大方便,但文檔收費,可以理解。 它還有一個相關的開源工程IReport。 IReport是一個圖形化的輔助工具,能彌補JasperReport的缺陷:JasperReport僅提供了可使用的類庫,而未提供更好的開發(fā)工具。它們配合使用將會更大程度的提高效率。 JasperReport基本架構JasperReport借由定義于XML文檔中的report design進行數據組織。 這些數據可能來自不同的數據源,包括關系型數據庫,collections,java對象數組。 通過實現簡單接口,用戶可以將report library插入到訂制好的數據源中。 JasperReports的報告模板可以以iReport之類的工具來制作,只要把報告儲存成XML格式,就可以讓JasperReport閱讀,然后再編譯成為.jasper檔。 JasperReports是世界上最流行的開源報告引擎。它是完全用Java編寫的,它是能夠使用的數據來自任何類型的數據源,并生成像素級的文檔,可以查看、打印或導出在多種文檔格式,包括HTML、PDF、excel、Openoffice和doc。 JasperReport使用方法第一步:XML解析 JasperReport使用SAX2.0 API對XML文件進行解析。然而,這并不是必須的,用戶可以在執(zhí)行其自行決定使用哪一種XML解析器。 JasperReport使用org.xml.sax.helpers.XMLReaderFactory類的createXMLReader()來獲得解析器實例。在這種情況下,就像在SAX2.0文檔中說的那樣,在運行期,把Java系統(tǒng)屬性org.xml.sax.driver(這是屬性的key)的值(value)設定為SAX driver類的全限定名是必要的。用戶可以通過兩種方法做到這一點,我稍后將解釋全部兩種方法。如果你想使用不同的SAX2.0XML解析器,你需要指定相應的解析器類的名字。 設置系統(tǒng)屬性的第一種方法是在你啟動Java虛擬機的時候,在命令行使用-D開關:java ?Dorg.xml.sax.driver=org.apache.serces.parsers.SAXParser mySAXApp sample.xml 在JasperReport提供的所有例子中,都采用ANT構建工具來執(zhí)行不同的任務。我們通過使用內置的 task中的元素來提供這一系統(tǒng)屬性: 第二種設置系統(tǒng)屬性的方法是使用java.lang.System.setProperty(String key, String value) System.setProperty(“org.xml.sax.driver”,” org.apache.xerces.parsers.SAXParser”); Jsp/compile.jsp和web-inf/class/servlets/CompileServlet.java文件提供了這方面的例子。 注:對于第二種方法,我要說些題外話。有關于JVM的系統(tǒng)屬性(我們可以通過System.out.println(System.getProperty(“PropertyKey”)來查看),可以在運行期像上面說所得那樣用System.setProperty(“propertyKey”,”propertyValue”);來進行設置。但是一旦JVM已經啟動之后,其內建的系統(tǒng)屬性,如user.dir,就不能再被更改。奇怪的是我們仍可以用System.setProperty()方法對其進行設置,而在用System.out.println(System.getProperty())方法進行查看的時候發(fā)現,其值已經更改為我們設置的值,但事實上我們設置的值不會起任何作用。所以對于內置的屬性,我們只能通過-D開關在JVM執(zhí)行之前進行設置。對于org.xml.sax.driver,由于它不是系統(tǒng)內建屬性,所以仍然可以在JVM啟動之后加以設置。更詳細的信息可以參考王森的〈Java深度歷險〉。 第二步:編譯報表設計(Report Designs) 為了生成一個報表,用戶需要首先生成報表的設計(report’s design),生成方法或采用直接編輯XML文件,或通過程序生成一個net.sf.jasper.engine.design.JasperDesign對象。本文中,我將主要采用編輯XML文件的方法,因為這種方法在目前是使用JasperReport類庫的最好的方法,并且我們有機會更好的了解類庫的行為。 先前提到過,XML報表設計是JasperReport用來生成報表的初級材料(raw meterial)。這是因為XML中的內容需要被編譯并載入到JasperDesign對象中,這些對象將在報表引擎向其中填入數據之前經過編譯過程。 注意:大多數時候,報表的編譯被劃歸為開發(fā)時期的工作。你需要編譯你的應用程序報表設計,就像你編譯你的Java源文件一樣。在部署的時候,你必須將編譯好的報表,連同應用程序一起安裝到要部署的平臺上去。這是因為在大多數情況下報表設計都是靜態(tài)的,很少用應用程序需要提供給用戶在執(zhí)行期編譯的,需要動態(tài)生成的報表。 報表編譯過程的主要目的是生成并裝載含有所有報表表達式(report expression)的類的字節(jié)碼。這個動態(tài)生成的類將會被用來在裝填數據,并給所有報表表達式求值(evaluate)的時候使用。具體例子是,如果你用IReport生成一個報表名字叫SimpleSheetTest,它的XML設計文件名叫SimpleSheetTest.jrxml,同時和它在同一目錄下IReport會自動生成一個文件名為SimpleSheetTest.java,里面主要是一些報表元素,如Field,Parameters,Variables的定義,以及一些求值表達式。當然,像上面提到的,這個文件在你直接使用JasperReport API的時候是看不到的,因為它是在執(zhí)行期生成的一個Class。要想看到它的辦法是:在IDE(JBuilder,Eclipse)中單步執(zhí)行程序,在報表打印的階段,你將能跟蹤到這個類,它的名字就是“你的報表名.java”,按上面的例子就是SimpleSheetTest.java,這和IReport是一致的。當然也可以像下面說的那樣,到生成這個類的臨時目錄里找到它。 在這個類生成過程之前,JasperReport引擎需要驗證報表設計的一致性(consistency),哪怕存在一處驗證檢查失敗都不會繼續(xù)運行下面的工作。在下面的章節(jié),我將會展示報表設計驗證成功之后的狀況。 對于這個包含了所有報表表達式(report expressions)的類的字節(jié)碼,我們至少需要關心三個方面的內容: 臨時工作目錄(temporary workingl directory) Java編譯器的使用: Classpath: 為了能夠編譯Java源文件,這個文件必須被創(chuàng)建并且被保存到磁盤上。Java編譯過程的輸出是一個.class文件,這個包含所有報表表達式的類在這個工作目錄里被創(chuàng)建并編譯,這也是為什么JasperReport需要訪問這個臨時目錄的原因。當報表的編譯過程結束之后,這些臨時的類文件將被自動刪除,而生成的字節(jié)碼將保存在net.sf.jasper.engine.JasperReport對象中。如果需要的話,這個類可以將自己序列化(serialized itself)并保存到磁盤上。這就是IReport的做法。 缺省情況下,這個臨時工作目錄就是啟動JVM時的當前目錄,這卻取決于JVM的系統(tǒng)屬性user.dir。通過更改系統(tǒng)屬性jasper.report.compile.temp,用戶可以很容易更改這個工作目錄。在Web環(huán)境下,特別是當你不想讓含有啟動Web Server的批處理文件的目錄和報表編譯過程的臨時工作目錄混在一起的時候,修改這個屬性就可以了。 上面提到的第二個方面涉及用來編譯報表表達式類的Java編譯器。首先,報表引擎將試圖使用sun.tools.javac.Main類來編譯Java源文件。這個類包含在tools.jar中,當且僅當這個jar文件在JDK安裝目錄下的bin/目錄中,或在classpath中時,sun.tools.javac.Main才能正常使用。 如果JasperReport不能成功裝載sun.tools.javac.Main文件,程序將動態(tài)執(zhí)行java編譯過程,就像我們通常用命令行那樣,使用JDK安裝目錄下的bin/目錄下的javac.exe。這就是為什么將JDK安裝目錄/lib/下的tools.jar文件copy到JasperReport工程的lib/目錄下是一個可選的操作(optional operation)。如果tools.jar不在classpath中,JasperReport將顯示錯誤信息并繼續(xù)上面提到的操作。 當編譯Java源文件的時候,最重要的事情莫過于classpath。如果Java編譯器不能在指定的classpath中找到它試圖編譯的所有相關類的源文件,則整個過程將失敗并停止,錯誤信息將在控制臺顯示出來。同樣的事情也將發(fā)生在JasperReport試圖編譯報表表達式類的時候。所以,在runtime為編譯過程提供正確的classpath是非常重要的。例如,我們我們需要確認在classpath中,我們提供了在報表表達式中可能用到的類(custom class)。 在這個方面也有一個缺省的行為。如果沒有為編譯report class特殊指定classpath,引擎將會使用系統(tǒng)屬性java.class.path的值來確定當前的JVM classpath。如果你指定了系統(tǒng)屬性jasper.reports.compile.class.path的值,你可以用你定義的classpath來覆蓋缺省行為。 大多數情況下,編譯一個report只需要簡單的調用JasperReport類庫中的JasperCompileManager.compileReport(myXmlFileName);即可。調用之后將生成編譯好的report design并存儲在.jasper文件中,這個文件將會保存在和提供XML report design文件相同的目錄中。 第三步:Report Design 預覽 JasperReport類庫并沒有提供高級的GUI工具來輔助進行設計工作。然而,JasperReport本身提供了一個很有用的可視化組件來幫助報表設計者在編譯的時候預覽報表設計(其實不如直接用IReport方便)。 net.sf.jasper.view.JasperDesigner是一個基于Swing的Java應用程序,它可以載入并顯示XML形式或編譯后的報表設計。盡管它不是一個復雜的GUI應用程序,缺乏像拖拽可視化報表元素這樣的高級功能,但是它仍然是一個有用的工具(instrument)。所有JasperReport工程提供的例子都利用了這個報表查看器(report viewer)。 如果你已經安裝了ANT(別告訴我你不知道什么是ANT),想要查看一個簡單的報表設計(JasperReport工程所帶例子),你只需要到相應的文件夾下輸入如下命令: 〉ant viewDesignXML 或者 〉ant viewDesign 如果你沒安裝ANT,要達到上面的效果就不是很容易,因為JasperReport本身需要一些其他輔助的jar包(在JasperReport安裝目錄/lib下),在運行的時候,你需要把這些jar包都包含到你的classpath里面,并且正確設計系統(tǒng)屬性,如上面提到的org.xml.sax.driver。我可以展示一下在windows下的例子: >java -classpath ./;../../../lib/commons-digester.jar; ../../../lib/commons-beanutils.jar;../../../lib/commons-collections.jar; ../../../lib/xerces.jar;../../../lib/jasperreports.jar -Dorg.xml.sax.driver=org.apache.xerces.parsers.SAXParser dori.jasper.view.JasperDesignViewer -XML -FFirstJasper.xml 很麻煩吧?還是趕快弄個ANT吧。下面是預覽之后的結果(其實用IReport更好) 第四步:報表裝填(Filling Report) 報表裝填(report filling)過程是JasperReport library最重要的功能。它體現了這個軟件最主要的目的(main objective),因為這一過程可以自由的操作數據集(data set),以便可以產生高質量的文檔。有3種材料需要裝填過程中作為輸入提供給JasperReport: report design(reportl templet) 參數(parameters)l 數據源(datal source) 這一過程的輸出通常是一個單一的最終要被查看,打印或導出到其他格式的文檔。 要進行這一過程,我們需要采用net.sf.jasper.engine.JasperFillManager類。這個類提供了一些方法來讓我們裝填報表設計(report design),report design的來源可以是本地磁盤,輸入流,或者直接就是一個已存在于內存中的net.sf.jasper.engine.JasperReport類。輸出的產生是于輸入類型相對應的,也就是說,如果JasperFillManager接到一個report design的文件名,裝填結束后生成的report將會是一個放在磁盤上的文件;如果JasperFillManager收到的是一個輸入流,則生成的report將會被寫道一個輸出流中。 有些時候,這些JasperFillManager提供的方法不能滿足某些特定的應用的要求,例如可能有人希望他的report design被作為從classpath中得到的資源,并且輸出的報表作為一個文件存放在一個指定的磁盤目錄下。遇到這種情況時,開發(fā)人員需要考慮在將報表設計傳遞給報表裝填過程之前,用net.sf.jasper.engine.util.JRLoader類來裝載report design對象。這樣,他們就能獲得像報表名這樣的report design屬性,于是開發(fā)者就能生成最終文檔的名字(construct the name of the resulting document),并將它存放到所需的位置上。 在現實中,有許多報表裝填的情境(scenarios),而裝填管理器僅試圖覆蓋其中最常被使用到的部分。然而對于想要自己定制裝填過程的人來說,只要采用上面所說的方法,任何開發(fā)者都可以達到滿意的結果。 報表參數通常作為java.util.Map的value提供給裝填管理器,參數名為其鍵值(key)。 作為裝填過程所需的第三種資源?數據源,有如下兩種情況: 通常,引擎需要處理net.sf.jasper.engine.JRDataSource接口的一個實例,通過這個實例,引擎可以在裝填過程中獲取所需數據。JasperFillManager提供的方法支持所有的JRDataSource對象(這是一個Interface,上面一章提到過它的常用實現)。 然而,這個管理器還提供一些接受java.sql.Connection對象作為參數的方法集,來取代所需的數據源對象。這是因為在很多情況下,報表生成所需的數據都來源于某個關系型數據庫中的表(table)。 在報表中,用戶可以提供SQL查詢語句來從數據庫中取回報表數據(report data)。在執(zhí)行期,engine唯一需要做的是獲得JDBCconnection對象,并使用它來連接想要連接的數據庫,執(zhí)行SQL查詢并取回報表數據。在后臺,引擎將使用一個特殊的JRDataSource對象,但是它對于調用它的程序來說是透明的。 JasperReport工程提供了相關的例子,它們采用HSQL數據庫服務器(在工程文件中,有一個相應的文件夾),要運行這些例子你需要首先啟動該服務器,方法是:在/demo/hsqldb目錄下輸入如下命令:>ant 或者 >ant runServer 沒裝ANT就麻煩點:>java -classpath ./;../../lib/hsqldb.jar org.hsqldb.Server 一下代碼片斷顯示了query例子是如何裝填數據的: //Preparing parameters Map parameters = new HashMap(); parameters.put("ReportTitle", "Address Report"); parameters.put("FilterClause", "'Boston', 'Chicago', 'Oslo'"); parameters.put("OrderClause", "City"); //Invoking the filling process JasperFillManager.fillReportToFile(fileName, parameters, getConnection()); 第五步:查看報表(Viewing Reports) 報表填充階段的輸出通常是一個JasperPrint對象,如果把它保存在磁盤上,通常以一個.jrprint文件的形式存在。JasperReport擁有一個內置的查看器,用來查看用內置的XML導出器(XML exporter)獲得的XML格式的報表文件。這個查看器就是以前提到過的net.sf.jasper.niew.JRViewer?一個基于Swing的應用程序組件,用戶可以通過繼承這個類來定制自己所需的查看器。JasperReport工程中自帶的例子webapp中,你可以閱讀JRViewerPlus類的代碼來獲取進一步內容。 注意:JasperViewer更像是一個教人們如何使用JRViewer組件的演示程序,這里要注意一點,當你調用JasperViewer的viewReport()方法來顯示報表時,如果你關閉了預覽Frame,整個應用程序將會隨之結束,因為這個函數最后調用了System.exit(0);你可以通過繼承這個類,并重新在你的Viewer里注冊java.awt.event.WindowListener來避免這一情況的發(fā)生。 第六步:打印報表 JasperReport類庫的主要目標,就是生成可打印的文檔。而且多數應用程序生成的報表都是需要落實(或打。┑郊垙埳。我們可以用net.sf.jasper.engine.JasperPrintManager來打印JasperReport生成的文檔。當然,報表也同樣可以在被導出到其他格式如PDF,HTML之后再被打印。通過JasperPrintManager提供的方法,我們可以打印整個文檔,打印單個文檔或打印某一范圍內的文檔,可以顯示打印對話框也可以不顯示。下面的例子演示了不顯示對話框,打印整個文檔的方法:JasperPrintManager.printReport(myReport,false); 這個例子顯示了如何打印5-11頁的文檔,同時顯示打印對話框:net.sf.jasper.engine.JasperPrintManager.printPages(myReport,4,10,true); 第七步:導出報表 在一些應用程序環(huán)境下,將JasperReport生成的文檔從其特有的格式導出到其他更為流行的格式如PDF,HTML是非常有用的。這樣一來,其他人就可以在沒有安裝JasperReport的情況下查看這些報表,特別是當這些文檔要通過網絡發(fā)送出去的時候。 JasperReport提供了JasperExportManager類來支持此項功能。這項功能將會在以后不斷加入對新的格式的支持。HTML和XML類型的文檔,下面是導出的代碼片斷:JasperExportManager.exortReportToHtmlFile(myReport); 注意:想要將自己的報表導出到其他格式的用戶,需要實現JRExporter的接口,或繼承相應的JRAbstractExporter類。 第八步:對象的載入和保存 當使用JasperReport的時候,你經常會與序列化的對象,如以編譯的報表設計,或已生成的報表打交道。有時,你需要手動載入從不同的source如input stream或你用類庫核心功能(lib’s core functionality)產生的序列化類。JasperReport提供了兩個特殊的工具類來提供上述操作的能力,這些類通常供報表引擎自己使用: net.sf.jasper.engine.util.JRLoader net.sf.jasper.engine.util.JRSaver 第一個類提供了一些方法讓我們能夠從不同類型的數據源如文件,URL,input stream和classpath里面獲取序列化對象。最令人感興趣的方法是loadObjectFormLocation(String)。它已經在上一章中介紹過了,這里不再贅述。 與上面的對象載入工具相反的部分是JRSaver類,它可以幫助程序員將自己的類序列化之后存放到本地磁盤或通過Output Stream發(fā)送到網絡上去。 有時,開發(fā)人員可能想要載入已經生成好的report,或最終的已經被導出到XML格式的JasperReport文檔,這與上面所說的直接load序列化對象有所不同。這時,我們需要載入的是將載入的XML內容進行編譯,并生成JasperPrint對象,而并非僅僅是載入序列化對象。這時,我們可以通過net.sf.jasper.engine.xml.JRPrintXmlLoader類的一些靜態(tài)方法,通過編譯從XML文件中讀取的內容構建出一個位于內存中的文檔對象。 |