23.09.2013 Views

JasperReports 報表製作入門

JasperReports 報表製作入門

JasperReports 報表製作入門

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

內 容<br />

<strong>JasperReports</strong> <strong>報表製作入門</strong><br />

<strong>JasperReports</strong> 簡介...............................................................................................1<br />

圖形化設計工具-iReport....................................................................................... 2<br />

設定 JDBC 資料來源............................................................................................2<br />

報表精靈............................................................................................................2<br />

報表細部微調......................................................................................................3<br />

資料欄位........................................................................................................ 3<br />

參數...............................................................................................................3<br />

變數...............................................................................................................3<br />

其它...............................................................................................................4<br />

報表預覽............................................................................................................4<br />

從程式中 生 報表 ................................................................................................... 4<br />

JasperPrint 存檔 ................................................................................................5<br />

與 TxtReport 印表系統結合.....................................................................................5<br />

圖形列印方式輸出................................................................................................... 6<br />

注意事項:新細明體............................................................................................ 6<br />

統計圖製作.............................................................................................................7<br />

結論......................................................................................................................7<br />

附錄 ......................................................................................................................8<br />

<strong>JasperReports</strong> 簡介<br />

<strong>JasperReports</strong> 官方網站(http://jasperreports.sourceforge.net)對計畫的簡介‥<br />

<strong>JasperReports</strong> is a powerful open source Java<br />

reporting tool that has the ability to deliver<br />

rich content onto the screen, to the printer or<br />

into PDF, HTML, XLS, CSV and XML files.<br />

It is entirely written in Java and can be used in<br />

a variety of Java enabled applications, including<br />

J2EE or Web applications, to generate dynamic<br />

content.<br />

Its main purpose is to help creating page<br />

oriented, ready to print documents in a simple<br />

and flexible manner.<br />

簡言之,<strong>JasperReports</strong> 可以靈活、有彈性地 生 我們所要的報表。彈性指得是可以<br />

用多種資料來源,如 Database、XML、JavaBean、CSV file…等等,亦可以輸出到印<br />

檔 表機、螢幕、多種 案格式。<br />

說<br />

接下來我們對報表的設計丶 生 丶輸出流程作 明。


圖形化設計工具-iReport<br />

<strong>JasperReports</strong> 報表 檔 為 XML 格式,用文字編輯器就可以設計 <strong>JasperReports</strong> 報表<br />

檔 。但這樣太麻煩,所以有人寫出視覺化報表設計程式,只要用滑鼠拉一拉就能完成設計<br />

(也沒這麼簡單啦 …),而<br />

iReport 就是其中之一。iReport 官方網站<br />

(http://ireport.sourceforge.net/)對計畫的簡介如下<br />

iReport is a powerful, intuitive and easy to use<br />

visual report builder/designer for <strong>JasperReports</strong><br />

written in 100% pure java. This tool allows users<br />

to visually edit complex reports with charts,<br />

images, subreports,.... iReport is integrated<br />

with JFreeChart, one of the most diffused<br />

OpenSource chart library for java. The data to<br />

print can be retrieved through several ways<br />

including multiple JDBC connections, TableModels,<br />

JavaBeans, XML, etc...<br />

下載完 iReport 並解壓縮後,執行 iReport.bat 即可 動 iReport。<br />

設定 JDBC 資料來源<br />

開始使用 iReport 設計報表前,首先要設定報表的資料來源。以下用 JDBC 連接<br />

Informix database 為例<br />

報表精靈<br />

● 設定 Informix JDBC driver class‥<br />

ifxjdbcdriver.jar。<br />

選單「工具 →Classpath」,加入<br />

● 選單「資料來源 →連結/資料來源」,建立新的資料來源。由上而下輸入<br />

• Type of connection: Database JDBC connection<br />

• Name: PCS test<br />

• JDBC Driver: com.informix.jdbc.IfxDriver<br />

• JDBC URL: jdbc:informixsqli://192.168.11.211:1526/pcstest:INFORMIXSERVER=nckmdbTest<br />

• Server Address: 192.168.11.211<br />

• Database: pcstest<br />

• Username: (不寫 …)<br />

• Password: (保密 …)<br />

● 完成後按下 Test 看連線有沒有成功。<br />

● 成功後按下 Save 儲存連線資訊。<br />

iReport 提供報表精靈,使用者跟著指引就可以做出報表的骨架。 生 的骨架只是把資<br />

料直接秀出來,需要進階報表的話還得做報表微調的工作。選擇選單「 檔<br />

案報表精靈」即<br />

→<br />

可 動 。<br />

步 報表精靈分五個 執行<br />

1. Query:輸入 SQL 查 詢字串,會送 SQL 到伺服器 資 料,順便檢 語 法正<br />

確與否。<br />

2. Fields selection:左欄列出 詢 得到的表格欄位,把用到的放到右欄中。<br />

3. Group By:目前還不知道怎麼用 步<br />

…有空再研究。可以直接到下一<br />

。<br />

4. Layout:有幾個 內<br />

建的樣版可以套用。也可以自己製做樣版。


報表細部微調<br />

5. Finish:顯示 Congratulation…<br />

在 <strong>JasperReports</strong> 中,文字顯示分為靜態文字與動態文字(Static text and<br />

Textfield)。顧名思意,靜態文字就是 內<br />

容不會變動的文字;而動能文字會根據資料做運<br />

算,再將結果顯示出來。動態文字運算時可以從「資料欄位」、「參數」及「變數」取得資<br />

料,利用 Java 語法進行算術運算、字串操作等等。<br />

介紹一下動態元素 資 料欄位、參數及變數<br />

資料欄位<br />

資料欄位(Field)就是我們從資料庫中 select 出來的欄位,使用格式為$F{欄位名<br />

稱}。iReport 會根據資料庫中的欄位型態,自動轉換為對應的 Java 型態,如<br />

varchar→ java.lang.String、decimal→<br />

java.math.BigDecimal…等等。<br />

舉個列子好了,資料庫存日期的格式是 char(8) 20060223 的話,可以利用下列運算<br />

轉成 95/02/23 供報表顯示<br />

(Integer.parseInt($F{in_date})/10000-1911)+"/"+<br />

$F{in_date}.substring(4,6)+"/"+<br />

$F{in_date}.substring(6,8)<br />

其中$F{in_date}就是資料庫欄位。<br />

參數<br />

參數(Parameter)是使用者傳入的資料。因為有些資料無法由資料庫取得,例如請<br />

求列印報表的使用者帳號。這類的資料在呼叫 JasperFillManager.fillReport()時傳入。<br />

參數也可以放在 SQL 查 詢命令中做為篩選條件,大多數報表都需要這麼做。例如<br />

變數<br />

select<br />

in_merge_reason,in_date,in_out_date,<br />

...<br />

from<br />

check_in,patient,doctor<br />

where<br />

...<br />

and (in_dr_no=$P{dr_no}<br />

or in_checkin_dr_no=$P{dr_no} )<br />

and check_in.in_hospital_no=$P{hospital_no}<br />

and in_date is not null and in_date''<br />

...<br />

內 建 變數(Variable)是在 生 報表時自動計算出的資料,如頁碼、頁數等。自定變<br />

數 可以把複雜的運算式寫在這裡,例如日期相減計算、案例個數統計 內<br />

…等。<br />

建有<br />

Count、Sum、Average、Lowest、Highest、StandardDeviation、Varience 等統<br />

計函數,可減少許多撰寫程式的工作。


其它<br />

比較常用的有橫線 …而已,其它元件待補。要注意的是有的時候得設定字型,才會顯示<br />

出中文字。還有文字框調太小的話,輸出時文字會消失。原因呢 …有待<br />

明 。<br />

報表預覽<br />

報表設計好了,接下來就是印出來看囉~這就要用到 iReport 的「建立」選單了。這<br />

個選單中列出了多種預覽輸出,如 JRViewer、HTML、PDF 等格式。在設計的時候用<br />

JRViewer 最方便,也最精確。如果表格設計得很擠,格與格之間貼在一起,很容易導致<br />

HTML 輸出時,文字欄位 內 容消失。至於 PDF,<strong>JasperReports</strong> 有秀出含日文字的成果,<br />

但中文字就是出不來 …待研究。<br />

選好預覽格式後,執行「建立執行報表(使用動態連結)」就會輸出報表了。要自動<br />

→<br />

開 報 表的話得在「工具選項外部程式」中設定對應的程式。<br />

→ →<br />

檔 報表設計結束後記得存 , <strong>JasperReports</strong> 報表副 檔 名為 .jrxml。<br />

從程式中 生 報表<br />

設計好的報表得放在應用系統中,這時候 iReport 就幫不上忙了,得自己寫程式讀入<br />

jrxml 檔 ,填入資料然後輸出到目的地。下面這段程式為 compile jrxml,參數 填 入,從<br />

資料庫抓取資料填入<br />

JasperReport jr;<br />

JasperPrint jp;<br />

Class.forName("com.informix.jdbc.IfxDriver").newInstance();<br />

Connection con = DriverManager.getConnection(url);<br />

Map para = new HashMap();<br />

para.put("dr_no","2115");<br />

para.put("hospital_no","1");//文件中有預設,為"1"。<br />

para.put("outpat_close_case","I9469210");<br />

jr = JasperCompileManager.compileReport<br />

("E:/workspace/NCKM_PCS/WebContent/JasperReport/jrxml/DoctorAndPatient.jrx<br />

ml");<br />

jp = JasperFillManager.fillReport(jr,para, con);<br />

產 生 JasperPrint 物件 jp 就是建立好的報表,用來輸出成多種格式。輸出成 XML 檔<br />

JasperExportManager.exportReportToXmlFile(jp,"e:/Export.xml",false);<br />

輸出成 HTML 格式 將 下列程式放到 JSP 中即可。<br />

//取得 JasperPrint 物件,輸出成網頁作預覽用。<br />

JRHtmlExporter exporter = new JRHtmlExporter();<br />

exporter.setParameter(JRExporterParameter.JASPER_PRINT, jp);<br />

//一定要改 SIZE_UNIT,不然字會小要看不到 …<br />

exporter.setParameter(JRHtmlExporterParameter.SIZE_UNIT,<br />

JRHtmlExporterParameter.SIZE_UNIT_POINT);<br />

//輸出到 out writer,也就是回傳的 內<br />

容。<br />

exporter.setParameter(JRExporterParameter.OUTPUT_WRITER , out);<br />

exporter.exportReport();<br />

目前只有用到這兩種,其它的以後再補充。記得把所需要的 JAR 都放好位置,才能<br />

檔<br />

正確執行。


JasperPrint 存檔<br />

製作好的報表若不需要立刻列印,那就存起來吧~JasperPrint 類別實作了<br />

java.io.Serializable,可以把物件直接輸出成 檔<br />

案。例如<br />

jp = JasperFillManager.fillReport(jr, para, con);<br />

// 將 JasperPrint 物件儲存,之後可以用。<br />

FileOutputStream fos = new FileOutputStream(<br />

"E:/workspace/DoctorAndPatient.jrprint");<br />

ObjectOutputStream oos = new ObjectOutputStream(fos);<br />

oos.writeObject(jp);<br />

oos.close();<br />

就是把報表存成 DoctorAndPatient.jrprint。讀出來列印也很簡單<br />

fis = new FileInputStream("E:/workspace/DoctorAndPatient.jrprint");<br />

ois = new ObjectInputStream(fis);<br />

jp = (JasperPrint) ois.readObject();<br />

JasperPrintManager.printReport(jp,true);<br />

與 TxtReport 印表系統結合<br />

現有的 TxtReport 印表伺服器漸趨完善,在還沒有建立 JasperReport 伺服器前,先<br />

利用 TxtReport 伺服器在印表機輸出。方式是將 JasperPrint 輸出成 XML 格式,再利用<br />

程式轉換成 TxtReport 指令。<br />

目前 JasperTxtTransformer 類別可以轉換「文字」和「橫線」。轉換時座標是取<br />

「格子」的左上角,所以用 iReport 製作報表時文字需對齊左上。<br />

示範如下<br />

//將 JasperReport 轉換為 TxtReport,並輸出到/txtrpt 目 錄檔 下的暫存 。<br />

String txtFilename = ReportUtil2.createTempFilename(report_name);<br />

FileWriter txtFile = new FileWriter(txtFilename);<br />

String txtPrinter =<br />

ReportUtil2.getHeaderStr(hospitalNo,report_name,termNo);<br />

ByteArrayOutputStream printXML = new ByteArrayOutputStream();<br />

JasperExportManager.exportReportToXmlStream(jp, printXML);<br />

ByteArrayInputStream pipe = new<br />

ByteArrayInputStream(printXML.toByteArray());<br />

JasperTxtTransformer.jasperToTxt(pipe, txtFile, txtPrinter);<br />

txtFile.close();<br />

ReportUtil2.ftp(txtFilename);<br />

另一個示範<br />

//將 JasperReport 轉換為 TxtReport<br />

String printer = "FinePrint pdfFactory;A4;9;11;1\n";<br />

ByteArrayOutputStream printXML = new ByteArrayOutputStream();<br />

JasperExportManager.exportReportToXmlStream(jp, printXML);<br />

ByteArrayInputStream pipe =<br />

new ByteArrayInputStream(printXML.toByteArray());<br />

ByteArrayOutputStream txt = new ByteArrayOutputStream();<br />

JasperTxtTransformer.jasperToTxt(pipe, txt, printer);<br />

//產 生 LineNumberReader 以便逐行讀入<br />

LineNumberReader reader = new LineNumberReader(


new StringReader(txt.toString()));<br />

圖形列印方式輸出<br />

若使用支援圖形列印的列表機(噴墨、雷射),可利用 Java 內 建的印表機制直接列印 ,<br />

在 JasperReport 中可使用 JasperPrintManager.printReport()來做。PrintReport()<br />

接受的參數為 JasperPrint 物件及印表機選擇框的 boolean 值 ,JasperPrint 物件就是剛<br />

才製做的報表;boolean 值 設 false 則報表直接由預設印表機輸出。例如<br />

JasperPrintManager.printReport(jp, true);<br />

會帶出印表機選擇框,選定印表機會就開始列印了。<br />

目前有兩個問題,其中之一就是選擇印表機一定得經由對話框,無法在程式中直接指定。<br />

這是因為 JRPrintAWT.printReport()彈性不足所造成,可以參考其源始碼自行改寫(繼<br />

承後覆蓋)。另一個問題是如何找到印表機,這得使用 javax.print 套件中的<br />

PrintServiceLookup 類別,傳入搜尋條件找出符合的印表機。例如以印表機名稱來找:<br />

String printer_name=”Kyocera FS-1900”;<br />

PrintServiceAttribute psat = new PrinterName(printer_name, Locale.TAIWAN);<br />

AttributeSet ats = new HashPrintServiceAttributeSet(psat);<br />

PrintService[] ps = PrintServiceLookup.lookupPrintServices(null, ats);<br />

for (int i = 0; i < ps.length; i++) {//應該只有一台;找到多台則用第一台。<br />

System.out.println(ps[i].getName());<br />

}<br />

至於 JWPrintAWT 的改寫,最簡單的方式為繼承後複製<br />

printPages(int,int,boolean) 內 容,寫出支援 printService 參數的<br />

printPages(int,int,PrintService)。注意要原本 printPages()的 boolean 參數沒?就是<br />

它決定是否跳出印表機對話框,故以傳入 PrintService 取代之。<br />

也可以寫個全新的類別,實作 java.awt.print.Printable 介面,完全取代<br />

JWPrintAWT 的功能。甚至使用新的套件 javax.print 來列印,但這樣會很多事情得自己<br />

動手,包括研究 javax.print 使用方法,故暫時打消這個念頭。<br />

注意事項:新細明體及統計圖<br />

Windows 的 Java 印表機制裡,送給印表機的文件中字元以「編碼」的方式儲存,如<br />

果字元為編碼中不存在的字,則用圖形方式存入。這樣新細明體而言有問題,因為新細明體<br />

存成圖形時會糊掉。換其它字型不就好了?問題是 JRE 預設的中文字型就是新細明體,除<br />

非在 iReport 設計報表時指定欄位的字型 …<br />

以上是使用 JRE 1.5 所遇到的問題。如果使用 JRE 1.4 版呢?那更慘 …全部以圖形方<br />

式存入,所以整個報表文字都很難看。應該是 java.awt 這個套件實作印表方式的關係。


比較會出問題的是全形標點符號,因為 iReport 使用 unicode,不小心就會輸入 big5<br />

碼之外的標點符號。Linux 下列印就沒這個問題了,因為 Linux 沒有新細明體 …但個人覺<br />

得新細明體印出來的報表比較好看。<br />

閱 讀。<br />

報表中有統計圖時,這種情形就無法避免。但實測結果字體只稍微模糊而已,並不影響<br />

統計圖製作<br />

因為沒有 JasperReport 與 iReport 官方使用手冊,所以這方面得自己摸索。目前會<br />

畫的只有長條圖,利用兩個變數來畫出天數「大於 100」及「小於 100」的案例個數。<br />

首先做出 InDays 變數,輸入計算天數的算式:<br />

new Long(<br />

(new Date().getTime()<br />

-new SimpleDateFormat("yyyyMMdd").parse($F{in_date},<br />

newParsePosition(0)).getTime()<br />

) /(24*3600*1000)//除以一天的 million second 數<br />

+1//算頭算尾<br />

)<br />

然後加入 count 計算類型的變數 greatThan100 與 lessThan100,算式分別填入<br />

$V{InDays}.intValue()>100?$V{InDays}:null<br />

以及 $V{InDays}.intValue()


附錄<br />

附錄一‥<br />

http://www.javaresearch.org/article/showarticle.jsp?column=151&thread=43306<br />

附錄二‥JasperReport 用户手册的翻譯及心得體會<br />

1 簡介<br />

該庫完全由 Java 寫成,可以用于在各種 Java 應用程式,含括 J2EE,Web 應用程式中生成動態内容。<br />

它的主要目的是輔助生成面向頁面的(page oriented),准備付諸列印的文檔。<br />

JasperReport 借由定義于 XML 文檔中的 report design 進行數據組織。這些數據可能來自不同的數據源,<br />

含括關系型數據庫,collections,java 對象數組。通過實現簡單的介面,用户可以 將 report library<br />

插入到訂制好的數據源中,在以後我們將提到有關内容。<br />

其實這是一份 JasperReport Ultimate Guide 的簡單翻譯以及我的理解和例子。在最後,我將說明<br />

一個我正在做的工程,將其中用到的相關資訊貢獻出來。我這麽做是因為當我在學這個類庫的時候苦 于<br />

很少有相關的中文文檔,誘惑語焉不詳,希望其他人不再受苦。這個文檔將分幾次貼出來,與原文檔的<br />

章節相對應。這份文檔的 Word 形式將在全部完成之後放 在我的公開郵箱中與各位共用。我的 EMail 是<br />

jxuedi@gmail.com 有什麽意見或想法請與我聯系。<br />

閑言少叙,進入正題。<br />

2 API 概覧<br />

Class net.sf.jasper.engine.design.JasperDesign<br />

這是一個未經加工的報表實例,供 JasperReport Library 使用。這個類可以在 JasperReport 類庫內建的<br />

XML 解析器對 XML report design 進行解析處理之後得到。如果你的程式不想對直接 XML 檔案進行作業,<br />

在例子 noxmldesign 中有不使用 XML 設計檔案而動態生成這個類 的方法。我們稍稍看看這個例子:<br />

public class NoXmlDesignApp{<br />

private static JasperDesign getJasperDesign() throws JRException{<br />

//JasperDesign 定義 JasperDesign 的頭資訊<br />

JasperDesign jasperDesign = new JasperDesign();<br />

jasperDesign.setName("NoXmlDesignReport");<br />

//Fonts 定義使用到的字體<br />

JRDesignStyle normalStyle = new JRDesignStyle();<br />

normalStyle.setName("Arial_Normal");<br />

//Parameters 定義 Parameters 的内容—這個内容以後會提到<br />

JRDesignParameter parameter = new JRDesignParameter();<br />

parameter.setName("ReportTitle");<br />

parameter.setValueClass(java.lang.String.class);<br />

jasperDesign.addParameter(parameter);<br />

parameter = new JRDesignParameter();<br />

parameter.setName("OrderByClause");<br />

parameter.setValueClass(java.lang.String.class);<br />

jasperDesign.addParameter(parameter);<br />

//Query 定義查詢<br />

JRDesignQuery query = new JRDesignQuery();<br />

query.setText("SELECT * FROM Address $P!{OrderByClause}");<br />

jasperDesign.setQuery(query);<br />

//Fields<br />

JRDesignField field = new JRDesignField();


field.setName("Id");<br />

field.setValueClass(java.lang.Integer.class);<br />

jasperDesign.addField(field);<br />

//Variables 定義變量<br />

JRDesignVariable variable = new JRDesignVariable();<br />

variable.setName("CityNumber");<br />

variable.setValueClass(java.lang.Integer.class);<br />

variable.setResetType(JRVariable.RESET_TYPE_GROUP);<br />

//Groups 定義組<br />

group.setMinHeightToStartNewPage(60);<br />

expression = new JRDesignExpression();<br />

//餘下定義一個文檔的其他内容,這裏省略<br />

return jasperDesign;<br />

}<br />

從 getJasperDesign()方法我們可以看出,這個應用程式并没有從 XML 檔案裏面將 report design 擷<br />

取出來在生成 JasperDesign 類,而是直接利用 JasperDesign 提供的函數生成了一個報表設計。這様做的<br />

原因是基于靈活性 的考慮,你可以在程式中隨時動態生成報表,而不需要去從硬盤或網絡中讀取 XML 設<br />

計檔案。但通常我不這麽做,因為比較麻煩,而且要對 JasperReport 的每個元素都非常熟悉才行。<br />

Class net.sf.jasper.engine.JasperReport<br />

這個類的實例包含了一個經過編譯的 report design 對象。生成它的時機是對報表編譯之後,但尚<br />

未對其填入數據的時候。編譯過程中,JasperReport 需要生成一個臨時的類檔案,用以儲存 report<br />

expression,如變量表達式,字檔,圖像表達式,組表達式等等。這個臨時的 Java Source File 是被動<br />

態編譯的,編譯器使用的是 JDK 中用來執行應用程式的編譯器類(compiler class)。如果 tools.jar<br />

不在 classpath 中,編譯過程將采用 javac.exe 來進行後台編譯。編譯後所得的字節碼儲存在<br />

JasperReport 類中,用來在執行期裝填數據(filling the report with data)和給表達式賦值<br />

(evaluate various report expression)。<br />

Class net.sf.jasper.engine.JasperCompileManager<br />

這是一個上面提到的與編譯有關的類。利用它提供的一些方法,你將有能力編譯從區域硬盤或一個 Input<br />

Stream 獲得的 XML report;還可以通過傳給 JasperCompileManager 一個 JasperDesign 類,來對記憶體<br />

中的 report design 進行編譯—功能很强大。<br />

Class net.sf.jasper.engine.JasperPrint<br />

當一個報表已經裝填好數據之後,這個文檔就以 JasperPrint 類的實例出現。這個類可以直接用<br />

JasperReport 內建的 viewer 進行查 看,也可以串列化到硬盤以備後用,或者發送到網上去。這個類的<br />

實例是報表裝填過程後的産物,它可以被 JasperReport 類庫中的導出方法導出成各種 流行的格式—<br />

PDF,HTML,XML 等等。<br />

Interface net.sf.jasper.engine.JRDataSource<br />

這個類與報表的數據源有關。只要能够恰當的實現他的一些介面,用户就可以在報表中使用各種數據源,<br />

在報表裝填的時候由報表引擎負責對數據進行解釋和獲取。當報表裝填的時候,報表引擎都會在後台生<br />

成或提供一個該介面的實例。<br />

Class net.sf.jasper.engine.JRResultSetDataSource<br />

這是一個 JRDataSource 的預設實現,因為很多報表數據都來源于關系數據庫,所以 JasperReport 預設包<br />

含了這個外覆(wrap)了 java.sql.ResultSet 對象的實現。<br />

這個類可以用來包裹(wrap)用以對報表進行裝填的、已經載入的結果集,也可以被報表引擎用來包裹<br />

通過 JDBC 執行完查詢後所得的數據----非常有用。<br />

Class net.sf.jasper.engine.data.JRTableModelDataSource<br />

顧名思義,這個類用于包裹 java.swing.table.TableModel 類中的數據,它也是實現了 JRDataSource 介<br />

面,用于在 Java Swing 程式中使用已經被載入到 table 中的數據來生成報表。


Class net.sf.jasper.engine.JREmptyDataSource<br />

這是 JRDataSouce 介面的最簡單實現,這個類用在不需要顯示數據源數據而從參數中獲取數據的報表和<br />

僅需要知道數據源中的實際行數(number of virtual rows)的報表中。<br />

JasperReport 自帶的例子:fonts,images,shapes 和 unicode 中使用這個類對報表進行裝填,來模擬没<br />

有任何 record 的數據源,這時所有的 field 都為 null。例如:<br />

JasperRunManager.runReportToPdfFile(fileName, null, new JREmptyDataSource());<br />

Class net.sf.jasper.engine.JasperFillManager<br />

這個類用來實現報表的數據裝填。這個類提供了很多方法來接受各種類型的 report design--可以是一個<br />

對象,一個檔案,或一個輸入流。它的輸出結果也是多様的:file,Object,output Stream。<br />

report 的裝填引擎需要接收一個可以從中獲取數據和 value 的數據源作為報表參數。參數值<br />

(Parameters value)通常使用 Java.util.Map 來提供,裏面包含的 KEY 是報表的參數名。<br />

數據源可以通過兩種方式提供,這取决于你的解决方案:<br />

通常情况下,用户應該提供一個 JRDataSource 對象,例如我前面提到的那些。<br />

但是大多數的報表都是采用關系數據庫中的值來裝填數據,所以 JasperReport 擁有一個內建的預設行為<br />

—讓用户在報表設計的時候提供一個 SQL 查 詢。在運行期,這個查詢將被執行以用來從數據庫中獲取要<br />

裝填的數據。在這種情况下,JasperReport 僅需要一個 java.sql.Connection 對象來取代通常的數據對<br />

象。JasperReport 需要這個連接對象來連接數據庫管理系統并執行查詢作業。<br />

在查詢結束之後,JasperReport 將自動生成一個 JRResultSetDataSource,并將它傳回給報表裝填過程。<br />

Class net.sf.jasper.engine.JRAbstractScriptlet<br />

這個類同様用于報表裝填期間,用户可以自己定義一些代碼,并由報表引擎在裝填過程中執行。這些用<br />

户代碼可以處理報表數據作業,或在一些定義好的時刻執行,例如 page,列,或組的分割處。<br />

Class net.sf.jasper.engine.JRDefaultScriptlet<br />

這是一個非常方便的 JRAbstractScriptlet 的子類。通常情况下你應該選擇繼承這個類。<br />

Class net.sf.jasper.engine.JasperPrintManager<br />

這個類用户提供列印方法,用户可以將整個文檔或部分文檔傳遞給它,也可以選擇是否顯示列印<br />

Dialog,這在他的 API 文檔中可以找到,這裏不再贅述。<br />

Class net.sf.jasper.engine.JasperExportManager<br />

顧名思義,這個類負責文檔的導出。這個類的具體資訊詳見 API 文檔。非常明顯和清除,没什麽好解釋<br />

的,Just use it 即可。<br />

Class net.sf.jasper.engine.JasperRunManager<br />

有時,我們僅僅需要構造一個流行的文檔格式,例如 PDF,或 HTML,而不需要將裝填過程後生成的<br />

JasperPrint 對象儲存到硬盤或其他中間媒體上。這時,可以使用這個類來直接將裝填過程生成的文檔導<br />

出到所需的格式。<br />

Class net.sf.jasper.view.JRViewer<br />

這是一個基于 Swing 的應用程式,你可以將它視為一個獨立組件,用來進行列印預覧。用户可以繼承這<br />

個類,來構造滿足自身要求的預覧程式。<br />

Class net.sf.jasper.view.JasperViewer<br />

這個類更像是使用 JRViewer 的教學組件,它演示了一個 Swing 應用程式如何裝在并顯示報表。<br />

Class net.sf.jasper.view.JasperDesignViewer<br />

這個類用于報表的設計期間,用來預覧報表模版。它僅作為一個開發工具存在于類庫中。<br />

Class net.sf.jasper.engine.util.JRLoader<br />

裝載器用于報表生成的各個主要階段—編譯,裝填等等。用户和引擎都可以利用這個類來裝載所需的串


列化對象如 file,URLs,intput stream 等等。這個類最令人感興趣的函數當屬<br />

loadOnjectFromLocation(String location)。當用户使用這個類從指定地點裝載對象的時候,該函數將<br />

首先將 location 解釋為一個合法的 URL,如果解析失敗,函數將認為所提 供的 location 是硬盤上的一<br />

個檔名,并將試圖讀取它。如果在指定地點没找到檔案,它將通過 classpath 定位一個相應于該<br />

location 的 資源,所有努力失敗之後,將抛出不規則。<br />

3 主要的任務和過程<br />

這一節我們將看到對你的 XML 報表設計進行解析,編譯,裝填數據,預覧結果和導出到其他格式的<br />

過程。<br />

3.1 XML 解析<br />

JasperReport 使用 SAX2.0 API 對 XML 檔案進行解析。然而,這并不是必須的,用于可以在執行其自<br />

行决定使用哪一種 XML 解析器。<br />

JasperReport 使用 org.xml.sax.helpers.XMLReaderFactory 類的 createXMLReader()來獲得 解析器<br />

實例。在這種情况下,就像在 SAX2.0 文檔中説的那様,在運行期,把 Java 系統屬性<br />

org.xml.sax.driver(這是屬性的 key) 的值(value)設定為 SAX driver 類的全限定名是必要的。用户可<br />

以通過兩種方法做到這一點,我稍後將解釋全部兩種方法。如果你想使用不同的 SAX2.0XML 解析器,你<br />

需要指定相應的解析器類的名字。<br />

設置系統屬性的第一種方法是在你啓動 Java 虚擬機的時候,在指令行使用-D 開關:java –<br />

Dorg.xml.sax.driver=org.apache.serces.parsers.SAXParser mySAXApp sample.xml<br />

在 JasperReport 提供的所有例子中,都采用 ANT 構建工具來執行不同的任務。我們通過使用內建的 task<br />

中的元素來提供這一系統屬性:<br />

第二種設置系統屬性的方法是使用 java.lang.System.setProperty(String key, String value)<br />

System.setProperty(“org.xml.sax.driver”,” org.apache.xerces.parsers.SAXParser”);<br />

Jsp/compile.jsp 和 web-inf/class/servlets/CompileServlet.java 檔案提供了這方面的例子。<br />

注:對于第二種方法,我要説些題外話。有關于 JVM 的系統屬性(我們可以通過 System.out.println<br />

(System.getProperty(“PropertyKey”)來檢視),可以在運行期像上面説所得那様用<br />

System.setProperty (“propertyKey”,”propertyValue”);來進行設置。但是一旦 JVM 已經啓動之後,<br />

其内建的系統屬性,如 user.dir,就 無法再被變更。奇怪的是我們仍可以用 System.setProperty()方<br />

法對其進行設置,而在用 System.out.println (System.getProperty())方法進行檢視的時候發現,其值<br />

已經變更為我們設置的值,但事實上我們設置的值不會起任何作用。所以對于內建 的屬性,我們只能通<br />

過-D 開關在 JVM 執行之前進行設置。對于 org.xml.sax.driver,由于它不是系統内建屬性,所以仍然可<br />

以在 JVM 啓動 之後加以設置。更詳細的資訊可以參考王森的〈Java 深度歷險〉。<br />

3.2 編譯報表設計(Report Designs)<br />

為了深成一個報表,用户需要首先生成報表的設計(report’s design),生成方法或采用直接編<br />

輯 XML 檔案,或通過程式生成一個 net.sf.jasper.engine.design.JasperDesign 對象。本文中,我將主<br />

要采用編輯 XML 檔案的方法,因為這種方法在目 前是使用 JasperReport 類庫的最好的方法,并且我們有<br />

機會更好的了解類庫的行為。<br />

先前提到過,XML 報表設計是 JasperReport 用來生成報表的初級材料(raw meterial)。這是因為<br />

XML 中的内容需要被編譯并載入到 JasperDesign 對象中,這些對象將在報表引擎向其中填入數據之前經<br />

過編譯過程。<br />

注意:大多數時候,報表的編譯被劃歸為開發時期的工作。你需要編譯你的應用程式報表設計,就<br />

像你編譯你的 Java 來源檔一様。在部署的時候,你必須將編譯 好的報表,連同應用程式一起安裝到要<br />

部署的平台上去。這是因為在大多數情况下報表設計都是静態的,很少用應用程式需要提供給用户在執<br />

行期編譯的,需要動態 生成的報表。<br />

報表編譯過程的主要目的是生成并裝載含有所有報表表達式(report expression)的類的字節碼。這個<br />

動態生成的類將會被用來在裝填數據,并給所有報表表達式求值(evaluate)的時候使用。具體例子是,<br />

如果 你用 IReport 生成一個報表名字叫 SimpleSheetTest,它的 XML 設計檔名叫<br />

SimpleSheetTest.jrxml,同時和它在 同一目録下 IReport 會自動生成一個檔名為<br />

SimpleSheetTest.java,裏面主要是一些報表元素,如 Field, Parameters,Variables 的定義,以及


一些求值表達式。當然,像上面提到的,這個檔案在你直接使用 JasperReport API 的時候是看不到的,<br />

因為它是在執行期生成的一個 Class。要想看到它的辦法是:在 IDE(JBuilder,Eclipse)中單步執行<br />

程式,在 報表列印的階段,你將能追蹤到這個類,它的名字就是“你的報表名.java”,按上面的例子<br />

就是 SimpleSheetTest.java,這和 IReport 是一致的。當然也可以像下面説的那様,到生成這個類的臨<br />

時目録裏找到它。<br />

在這個類生成過程之前,JasperReport 引擎需要驗證報表設計的一致性(consistency),哪怕存在<br />

一處驗證檢查失敗都不會繼續運行下面的工作。在下面的章節,我將會展示報表設計驗證成功之後的狀<br />

况。<br />

對于這個包含了所有報表表達式(report expressions)的類的字節碼,我們至少需要關心三個方面的<br />

内容:<br />

臨時工作目録(temporary working directory)<br />

Java 編譯器的使用<br />

Classpath<br />

為了能够編譯 Java 來源檔,這個檔案必須被創建并且被儲存到磁盤上。Java 編譯過程的輸出是一個.<br />

class 檔案,這個包含所有報表表達式的類 在這個工作目録裏被創建并編譯,這也是為什麽<br />

JasperReport 需要訪問這個臨時目録的原因。當報表的編譯過程結束之後,這些臨時的類檔案將被自動<br />

移除,而生成的字節碼將儲存在 net.sf.jasper.engine.JasperReport 對象中。如果需要的話,這個類<br />

可以將自己串列化 (serialized itself)并儲存到磁盤上。這就是 IReport 的做法。<br />

預設情况下,這個臨時工作目録就是啓動 JVM 時的當前目録,這却取决于 JVM 的系統屬性 user.dir。通<br />

過變更系統屬性 jasper.report.compile.temp,用户可以很容易變更這個工作目録。在 Web 環境下,特<br />

彆是當你不想讓含有啓動 Web Server 的批處理檔案的目録和報表編譯過程的臨時工作目録混在一起的時<br />

候,修改這個屬性就可以了。<br />

上面提到的第二個方面涉及用來編譯報表表達式類的 Java 編譯器。首先,報表引擎將試圖使用<br />

sun.tools.javac.Main 類來編譯 Java 源 檔案。這個類包含在 tools.jar 中,當且僅當這個 jar 檔案在<br />

JDK 安裝目録下的 bin/目録中,或在 classpath 中時, sun.tools.javac.Main 才能正常使用。<br />

如果 JasperReport 無法成功裝載 sun.tools.javac.Main 檔案,程式將動態執行 java 編譯過程,就像我<br />

們通常用指令行那様, 使用 JDK 安裝目録下的 bin/目録下的 javac.exe。這就是為什麽將 JDK 安裝目録<br />

/lib/下的 tools.jar 檔案 copy 到 JasperReport 工程的 lib/目録下是一個可選的作業(optional<br />

operation)。如果 tools.jar 不在 classpath 中,JasperReport 將顯示錯誤資訊并繼續上面提到的作業。<br />

當編譯 Java 來源檔的時候,最重要的事情莫過于 classpath。如果 Java 編譯器無法在指定的<br />

classpath 中找到它試圖編譯的所有相關類的 來源檔,則整個過程將失敗并停止,錯誤資訊將在主控台<br />

顯示出來。同様的事情也將發生在 JasperReport 試圖編譯報表表達式類的時候。所以,在 runtime 為編<br />

譯過程提供正確的 classpath 是非常重要的。例如,我們我們需要確認在 classpath 中,我們提供了在報<br />

表表達式中可能用到 的類(custom class)。<br />

在這個方面也有一個預設的行為。如果没有為編譯 report class 特殊指定 classpath,引擎將會使<br />

用系統屬性 java.class.path 的值來確定當前的 JVM classpath。如果你指定了系統屬性<br />

jasper.reports.compile.class.path 的值,你可以用你定義的 classpath 來覆蓋預設行為。<br />

大多數情况下,編譯一個 report 只需要簡單的調用 JasperReport 類庫中的<br />

JasperCompileManager.compileReport(myXmlFileName);即可。調用之後將生成編譯好的 report design<br />

并存儲在.jasper 檔案中,這個檔案將會儲存在和提供 XML report design 檔案相同的目録中。<br />

3.3 Report Design 預覧<br />

JasperReport 類庫并没有提供高級的 GUI 工具來輔助進行設計工作。但是目前已經有至少 4 個 project<br />

試圖提供這様的工具。然而, JasperReport 本身提供了一個很有用的可視化組件來幫助報表設計者在編<br />

譯的時候預覧報表設計(其實不如直接用 IReport 方便)。<br />

3.4 報表裝填(Filling Report)<br />

報表裝填(report filling)過程是 JasperReport library 最重要的功能。它體現了這個軟件最主要的<br />

目的(main objective),因為這一過程可以自由的作業數據集(data set),以便可以産生高質量的<br />

文檔。有 3 種材料需要裝填過程中作為輸入提供給 JasperReport:<br />

report design(report templet)


參數(parameters)<br />

數據源(data source)<br />

這一過程的輸出通常是一個單一的最終要被檢視,列印或導出到其他格式的文檔。<br />

要進行這一過程,我們需要采用 net.sf.jasper.engine.JasperFillManager 類。這個類提供了一些方法<br />

來讓我們裝填報表 設計(report design),report design 的來源可以是區域磁盤,輸入流,或者直接<br />

就是一個已存在于記憶體中的 net.sf.jasper.engine.JasperReport 類。 輸出的産生是于輸入類型相對<br />

應的,也就是説,如果 JasperFillManager 接到一個 report design 的檔名,裝填結束後生成的 report<br />

將會是一個放在磁盤上的檔案;如果 JasperFillManager 收到的是一個輸入流,則生成的 report 將會被<br />

寫道一個輸出流中。<br />

有些時候,這些 JasperFillManager 提供的方法無法滿足某些特定的應用的要求,例如可能有人希望他<br />

的 report design 被作為從 classpath 中得到的資源,并且輸出的報表作為一個檔案存放在一個指定的磁<br />

盤目録下。遇到這種情况時,開發人員需要考慮在將報 表設計傳遞給報表裝填過程之前,用<br />

net.sf.jasper.engine.util.JRLoader 類來裝載 report design 對象。這様,他們就能獲得像報表名這<br />

様的 report design 屬性,于是開發者就能生成最終文檔的名字(construct the name of the<br />

resulting document),并將它存放到所需的位置上。<br />

在現實中,有許多報表裝填的情境(scenarios),而裝填管理者僅試圖覆蓋其中最常被使用到的部分。<br />

然而對于想要自己自訂裝填過程的人來説,只要采用上面所説的方法,任何開發者都可以達到滿意的結<br />

果。<br />

報表參數通常作為 java.util.Map 的 value 提供給裝填管理者,參數名為其鍵值(key)。<br />

作為裝填過程所需的第三種資源—數據源,有如下兩種情况:<br />

通常,引擎需要處理 net.sf.jasper.engine.JRDataSource 介面的一個實例,通過這個實例,引擎可以<br />

在裝填過程中獲取所需數 據。JasperFillManager 提供的方法支援所有的 JRDataSource 對象(這是一個<br />

Interface,上面一章提到過它的常用實 現)。<br />

然而,這個管理者還提供一些接受 java.sql.Connection 對象作為參數的方法集,來取代所需的數據源<br />

對象。這是因為在很多情况下,報表生成所需的數據都來源于某個關系型數據庫中的表(table)。<br />

在報表中,用户可以提供 SQL 查詢語句來從數據庫中取回報表數據(report data)。在執行期,engine<br />

唯一需要做的是獲得 JDBC connection 對象,并使用它來連接想要連接的數據庫,執行 SQL 查詢并取回報<br />

表數據。在後台,引擎將使用一個特殊的 JRDataSource 對 象,但是它對于調用它的程式來説是透通的。<br />

JasperReport 工程提供了相關的例子,它們采用 HSQL 數據庫服務器(在工程檔案中,有一個相應的檔案<br />

夾),要運行這些例子你需要首先啓動該服 務器,方法是:在/demo/hsqldb 目録下輸入如下指令:>ant<br />

或者 >ant runServer<br />

没裝 ANT 就麻煩點:>java -classpath ./;../../lib/hsqldb.jar org.hsqldb.Server<br />

一下代碼片斷顯示了 query 例子是如何裝填數據的:<br />

//Preparing parameters<br />

Map parameters = new HashMap();<br />

parameters.put("ReportTitle", "Address Report");<br />

parameters.put("FilterClause", "'Boston', 'Chicago', 'Oslo'");<br />

parameters.put("OrderClause", "City");<br />

//Invoking the filling process<br />

JasperFillManager.fillReportToFile(fileName, parameters, getConnection());<br />

3.5 檢視報表(Viewing Reports)<br />

報表填補階段的輸出通常是一個 JasperPrint 對象,如果把它儲存在磁盤上,通常以一個.jrprint 檔案<br />

的形式存在。JasperReport 擁有一個內建的檢視器,用來檢視用內建的 XML 導出器(XML exporter)獲<br />

得的 XML 格式的報表檔案。這個檢視器就是以前提到過的 net.sf.jasper.niew.JRViewer—一個基于<br />

Swing 的應用程式組件,用户可以通過繼承這個類來自訂自己所需的檢視器。JasperReport 工程中自帶<br />

的例子 webapp 中,你可以閲讀 JRViewerPlus 類的代碼來獲取進一步内容。<br />

注意:JasperViewer 更像是一個教人們如何使用 JRViewer 組件的示範程式,這裏要注意一點,當你調用<br />

JasperViewer 的 viewReport()方法來顯示報表時,如果你關閉了預覧 Frame,整個應用程式將會隨之結<br />

束,因為這個函數最後調用了 System.exit (0);你可以通過繼承這個類,并重新在你的 Viewer 裏註冊<br />

java.awt.event.WindowListener 來避免這一情况的發生。


3.6 列印報表<br />

JasperReport 類庫的主要目標,就是生成可列印的文檔。而且多數應用程式生成的報表都是需要落實<br />

(或列印)到紙張上。我們可以用 net.sf.jasper.engine.JasperPrintManager 來列印 JasperReport 生<br />

成的文檔。當然,報表也同様可以在被 導出到其他格式如 PDF,HTML 之後再被列印。通過<br />

JasperPrintManager 提供的方法,我們可以列印整個文檔,列印單個文檔或列印某一範 圍内的文檔,可<br />

以顯示列印對話框也可以不顯示。下面的例子演示了不顯示對話框,列印整個文檔的方法:<br />

JasperPrintManager.printReport(myReport,false);<br />

這個例子顯示了如何列印 5-11 頁的文檔,同時顯示列印對話框:<br />

net.sf.jasper.engine.JasperPrintManager.printPages(myReport,4,10,true);<br />

3.7 導出報表<br />

在一些應用程式環境下,將 JasperReport 生成的文檔從其特有的格式導出到其他更為流行的格式如<br />

PDF,HTML 是非常有用的。這様一來,其他人就可以在没有安裝 JasperReport 的情况下檢視這些報表,<br />

特彆是當這些文檔要通過網絡發送出去的時候。<br />

JasperReport 提供了 JasperExportManager 類來支援此項功能。這項功能將會在以後不斷加入對新的格<br />

式的支援。目前, JasperReport 主要支援導出 PDF,HTML 和 XML 類型的文檔,下面是導出的代碼片斷:<br />

JasperExportManager.exortReportToHtmlFile(myReport);<br />

注意:想要將自己的報表導出到其他格式的用户,需要實現 JRExporter 的介面,或繼承相應的<br />

JRAbstractExporter 類。<br />

3.8 對象的載入和儲存<br />

當使用 JasperReport 的時候,你經常會與串列化的對象,如以編譯的報表設計,或已生成的報表打交道。<br />

有時,你需要手動載入從不同的 source 如 input stream 或你用類庫核心功能(lib’s core<br />

functionality)産生的串列化類。JasperReport 提供了兩個特殊的工具類來提供上述作業的能力,這些<br />

類通常供報表引擎自己使用:<br />

net.sf.jasper.engine.util.JRLoader<br />

net.sf.jasper.engine.util.JRSaver<br />

第一個類提供了一些方法讓我們能够從不同類型的數據源如檔案,URL,input stream 和 classpath 裏面<br />

獲取串列化對象。最令人感興趣的方法是 loadObjectFormLocation(String)。它已經在上一章中介紹過<br />

了,這裏不再贅述。<br />

與上面的對象載入工具相反的部分是 JRSaver 類,它可以幫助程式員將自己的類串列化之後存放到區域<br />

磁盤或通過 Output Stream 發送到網絡上去。<br />

有時,開發人員可能想要載入已經生成好的 report,或最終的已經被導出到 XML 格式的 JasperReport 文<br />

檔,這與上面所説的直接 load 串列 化對象有所不同。這時,我們需要載入的是將載入的 XML 内容進行編<br />

譯,并生成 JasperPrint 對象,而并非僅僅是載入串列化對象。這時,我們可以通 過<br />

net.sf.jasper.engine.xml.JRPrintXmlLoader 類的一些静態方法,通過編譯從 XML 檔案中讀取的内容構<br />

建出一個 位于記憶體中的文檔對象。<br />

4 報表設計(Report Designs)<br />

報表設計體現了一個模版,JasperReport 引擎利用這個模版將同台生成的内容傳遞給列印機,螢幕或<br />

Web。存儲在數據庫中的數據在報表裝填的過程過被組織起來,根據已有的報表設計來獲得可以進行列印<br />

的,面向頁面的(page oriented)文檔。<br />

總而言之,一個報表設計包含了所有的結構相關資訊和將數據提供給報表所涉及的各個方面。這些資訊<br />

涉及將在文檔中顯示出來的各種 text 或圖像元素的位置和内容,自訂義計算(custom calculation),<br />

數據組織,報表生成時的數據作業,等等。<br />

報表設計通常都定義在一個擁有特殊格式的 XML 文檔中,并且在被填補數據之前要經歷 JasperReport 的<br />

編譯過程,有關于這個 XML 文檔的詳細資訊 我們將在以後説明。然而 JasperReport 也允許用户通過<br />

JasperReport 提供的 API 構造 in-memory 報表對象,例程 noxmldesign 就是很好的例子,但是我們通常<br />

不這麽用。<br />

4.1 DTD Reference


當使用 XML 檔案進行報表設計的時候,JasperReport 將使用內建的 DTD 檔案來驗證其受到的 XML 内容的<br />

有效性。如果 XML 驗證通過,則説明所 提供的報表設計符合 JasperReport 所需要的 XML 結構和語法規則,<br />

其引擎能够生成經過編譯的 report design。<br />

有效的 XML 文檔總是在驗證時指向 JasperReport 的内部 DTD 檔案。如果没有提供 DTD 文檔的參照,報表<br />

的編譯過程將會突然結束。這對所有人來 説都是一個負擔,因為 DTD 參照通常是相同的,并且這些參照<br />

可能會簡單的被從以前的報表設計中 copy 過來。在一開始,你需要將這個參照從給定的例子中 copy 過<br />

來。<br />

正如以前説的一様,報表引擎僅能識彆指向其内部 DTD 檔案的的參照。你無法隨便從類庫的來源檔中將<br />

那些 DTD 檔案 copy 到彆的地方,再在你的報表設計文 件中檔案中指向你 copy 的那些 DTD 檔案。如果你<br />

想那様做的話,你將需要調整類庫中某些類,含括 net.sf.jasper.engine.xml.JRXmlDigester 類的某些<br />

代碼。如果你遇到像引擎無法找到其内部的 DTD 檔案而導致的無法 載入資源的問題,請確定你已經在使<br />

用外部 DTD 檔案之前排除了所有可能發生的情况。遇到這様的問題是不太可能的,因為資源載入機制會<br />

隨着時間不斷改進。 JasperReport 只有兩種合法的 XML 報表設計的 DTD 參照,他們是:<br />

或者<br />

4.2 XML 編碼<br />

當要生成不同語言的 XML 報表設計的時候,在 XMl 檔案的首部的編碼屬性需要特彆關注一下。預設情况下,<br />

如果這個屬性的值没被訂制,則 XML 解析器將會使 用“UTF-8”作為 XML 檔案的編碼格式。這一點是非常<br />

重要的,因為報表設計通常包含了静態的本土化 text。對于大多數西歐語言來説,ISO- 8859-1 編碼,<br />

也就是我們常説的 LATIN1 將會很好的處理如法語中重音符號的顯示問題。<br />

在編輯 XML 檔案的時候,要找到某種特殊語言的編碼類型,你可以檢視 XML document.FIXME<br />

4.3 報表屬性<br />

我們上面已經看到,斯 XML 報表設計的根元素。這一節我將介紹報表設計對象的 Property 的細節以及這<br />

些屬性所對應的 XML attributes(為避免混淆,我將不提供 Property 和 Attribute 的中文而直接使用英<br />

文)。<br />

Report Name<br />

每一個報表都必須有一個名字。這個名字是相當重要的,因為類庫需要它來生成檔案,尤其是當編譯,<br />

裝填,導出報表的默認行為被使用的時候,這個名的作用更為重要。這個名字是以元素的 name<br />

attribute 的形式提供的,并且是强制必須填寫。<br />

Column Count(列數)<br />

JasperReport 允許生成每頁的列數超過一列的報表,正如下面的圖片,展示了擁有兩列的報表:<br />

默認情况下,報表引擎生成每頁一列的報表。<br />

Print Order(列印順序)<br />

對于擁有超過一列的報表,為其提供列將被以什麽順序填補是很重要的。你可以使用的 printOrder<br />

attribute 來進行設置。有如下兩種情况:<br />

Vertical Filling:這個選項將導致列是自頂向下被填補(printOrder=”Vertical”)<br />

Horzontal:列將自左向右被填補(printOrder=”Horizontal”)<br />

預設設置將是 printOrder=Vertical<br />

Page Size(頁面大小)<br />

有兩個 attribute 是用來提供要生成的文檔大小的:pageWith,pageHeight。像所有其他顯示元素位置<br />

和大小的 attribute 一 様,這兩個 attribute 是以像素為單位的。JasperReport 采用 Java 默認的每英<br />

吋 72 點的設置。這意味着 pageWith=”595” 將大约是 8.26 英吋,這大概是 A4 紙的大小。<br />

默認的紙張大小是 A4 紙:pageWith=”595” pageHeight=”842”<br />

Page Orientation(默認設置為 Portrait)


orientation 屬性用來設置文檔列印格式是“Portrait”還是“Landscape”。JasperReport 允許用户在<br />

從 “Portrait”切换到“Landscape”的時候調整液面的寬度和高度。我們先看一個例子:我們假定要<br />

生成一個 A4 紙的報表,采用 “Protrait”格式。<br />

pageWidth=”595” pageHeight=”842” orientation=”Portrait”<br />

如果我們决定用 A4 紙的“Landscape”佈局,首先要調整相應的頁面寬度和高度:<br />

pageWidth=”842” pageHeight=”595” orientation=”Landscape”<br />

這是因為 JasperReport 需要確切知道它所要繪制的報表頁的寬度和高度,而不只看我們提供的<br />

orientation 屬性,至少在報表裝填的時候是 這様。orientation 屬性僅在報表列印時有用,來通知列<br />

印機或某些 exporters 頁面的 orientation 設置。<br />

Page Margins(頁邊距)<br />

一旦頁面大小確定下來,用户就可以在生成報表的時候設定報表的邊距。有四個屬性來完成這項工作:<br />

topMargin,LeftMargin,bottomMargin 和 rightMargin。預設的設置是上下邊距 20 像素,左右邊距 20<br />

像素。<br />

Column Size and Spacing(列寬和列間距)<br />

一個報表可能含有多列,我們可以通過上面提到的 columnCount 屬性得到報表列數。JasperReport 需要<br />

知道列的寬度和列間距的大小。有兩 個屬性用于這項工作:columnWidth 和 columnSpacing。當我們對<br />

報表設計進行編譯的時候,編譯器會對這項設置進行有效性檢查 (validation check)--看列的寬度和<br />

列間距是否符合給定的頁面寬度和頁邊距。因為預設的列數為一,所以預設的列間距為 0 像素,并且預<br />

設的列寬等于頁面寬度减去左 右邊距所得的值。在上面 A4 的例子中,列寬即為 555 像素。<br />

Empty Data Source Behavior<br />

有時我們提供給我們的報表的數據源可能會没有任何 record。whenNoDataType 屬性可以讓你選擇當所提<br />

供的數據源中没有數據的時候入和察看生成的報表。如下有三種不同的可能性,你可以任選其一:<br />

Empty Document:生成的報表不含有頁面(no page in it)。當你試圖裝載這様的文檔<br />

(whenNoDataType=”NoPages”)的時候,Viewer 可能會抛出一個錯誤。<br />

Blank page:表示生成的報表將僅含有一個空白頁。(whenNoDataType=”BlankPage”)<br />

All sections displayed:除了 detail 部分的其他部分將在生成的文檔中顯示出來。<br />

(whenNoDataType=”AllSectionNoDetail”)。<br />

預設的設置是 whenNoDataType=”NoPages”。<br />

Title and Summary Sections Placement(標題和摘要的放置)<br />

如果你想讓 title 部分和 summary 部分在單獨的一頁裏顯示,你所需要做的事情就是讓下面的一個或兩個<br />

屬性的值為“true”:isTitleNewPage,isSummaryNewPage。這兩個屬性預設情况下為 false。<br />

注意:即使你選擇了在最後一頁的剩餘部分顯示 summary,如果列數超過一列,并且第二列已經在最後一<br />

頁出現的時候(没試過,等有機會試驗一下),新的一頁將會被自動生成。<br />

Scriptlet Class<br />

scirptletClass 屬性用于設置用于當前報表的 scriptlet 類的名字。在以後我會對 Scriptlet 進行詳細<br />

討論。如果你没為這個屬性 提供任何值,報表引擎將會使用<br />

net.sf.jasper.engine.JRDefaultScriptlet 的實例。<br />

5 報表數據(Report Date)<br />

當我們談到報表裝填過程的時候,有三様東西需要作為輸入提供給報表引擎:報表設計(report<br />

design),參數值(parameter values)和報表的數據源(data source)。<br />

在先前的章節,我們已經看到了有關報表設計的某些方面,現在我們要更加詳細的關注其他兩方面的内<br />

容:參數(parameter)和報表數據源。他們描繪了 報表引擎在裝填報表過程中所用到的僅有的數據來<br />

源。像你用其他報表工具所希望的一様,這些數據將會根據報表設計中的定義的模版(template)被組<br />

織 起來,并被用來生成准備列印的、面向頁面的文檔。<br />

5.1 表達式(expressions)<br />

表達式是 JasperReport 的一個非常有用的特徴。他們可以用來聲明執行各種執行各種計算<br />

(calculations)的報表變量(report variables),進行報表的數據組織,自訂報表字檔欄位(text


field)的内容或者進一步自訂報表對象的 appearance。<br />

所有報表表達式基本上都是 Java 表達式,他們可以以特殊的語法參照報表參數(parameter),報表欄<br />

位(field)和報表變量(variable)。在 XML 報表設計中,有一些用于定義表達式的元素:,,,,,<br />

等等<br />

因為所有的 JasperReport 表達式都是真正(real)的 Java 表達式,只要你用完整的類名(含括包名)來<br />

參照這些表達式,你就可以在任何 class 中使用他們。當你編譯報表和裝填數據的時候,你應該確定你<br />

在報表表達式中使用的類已經寫入了 classpath。<br />

報表參數參照是通過$P{}串列引入的,例如:<br />

$P{ReportTitle}<br />

這個例子假定我們在報表設計中有一個名為 ReportTitle 的報表參數,這個參數是一個<br />

java.lang.String 類。當報表進行裝填的時候,字檔欄位將會顯示這個參數的值。<br />

為了在一個表達式中使用報表欄位,欄位名必須放在$F{}的括號中。例如,如果我們想要在一個字檔欄<br />

位中顯示兩個數據源欄位的連接值(concatenated values),我們可以定義如下表達式:<br />

$F{FirstName} + “ “ + $F{LastName}<br />

表達式可以更加復雜:<br />

$F{FirstName} + “ “ + $F{LastName} + “ was hired on “ +<br />

(new SimpleDateFormat(“MM/DD/YYYY”)).format($F{HireDate)) + “.”<br />

正如你所看到的一様,參數、欄位和變量參照都是通過用 JasperReport 的特殊語法從一個真正地 Java 對<br />

象中引入的。(實際上是一個 JREvaluator 對象,該對象是在運行其生成的動態對象,并無法在區域磁<br />

盤上見到它的身影,不過如果你使用 iReport 的話,你就可以在生成報表 檔案的同一目録下看到它的本<br />

來面目)<br />

5.2 參數(Parameters)<br />

Parameters 是傳遞給報表裝填作業的對象參照。這些參數主要作用于把那些無法從報表數據源中獲得的<br />

數據傳給報表引擎。例如,我們可能要把執行報表 裝填過程的用户名字專遞給報表引擎,如果我們想讓<br />

它顯示在報表上或者在我們想在報表的 title 上動態的改變它,我們就可以以參數的形式傳給報表引擎。<br />

我們可以用如下的方式定義參數:<br />

這裏所提供的報表參數值可以被用到各種報表表達式中,在報表 SQL 查詢中,甚至可以用到報表的<br />

scriptlet 類裏。下面是構成參數定義的全部組件(XML 元素):<br />

參數名<br />

name 屬性是一個强制屬性。JasperReport 的命名習慣和 Java 語言的命名習慣是類似的,這意味着參數名<br />

應該是一個單詞,其中不含特殊字元(如分號)。<br />

參數類型<br />

報表參數的第二個强制屬性是提供參數值的類型名。這個類型屬性可以使任意的值,只要這個類型的名<br />

字在報表編譯期和裝填期能在 classpath 中找到即可。<br />

Prompting for Parameter values<br />

在 GUI 參照程式中,建立一個報表參數集,讓用户在執行裝填過程之前輸入某些應用程式需要用户輸入<br />

的報表參數是很有用的。可選參數 isForPropting 參數用來聲明是否顯示輔助說明資訊讓用户輸入某些<br />

參數。下面的例子中,我聲明了一個字檔參數,當需要用户輸入參數值的時候,這個文 本參數用來在一<br />

個已自訂的對話框中說明需要用户輸入什麽様的參數。


注意:相信大家都知道表示“内容”將不被 XML 解析器解析,也就是説,你可以在“内容”裏加入 XML 的<br />

特殊字元,如>,


一 下:<br />

Class net.sf.jasper.engine.JRResultSetDataSource<br />

這是一個非常有用的預設實現,因為他外覆(wrap)了 java.sql.ResultSet 對象。由于多數報表的生成<br />

都采用關系數據庫中存儲的數據,所以這個類是被使用得最為廣泛的數據源對象。然而在以下的兩種情<br />

况下您可以不必在裝填過程中自己生成這個對象:<br />

如果你選擇在你的報表中用 SQL 查詢來獲得在關系數據庫的某個 table 中的數據,報表引擎將會通過執行<br />

給定的 SQL 查詢并且將傳回的 java.sql.ResultSet 外覆為一個<br />

net.sf.jasper.engine.JRResultSetDataSource 實例來執行這項 作業。引擎唯一需要的是一個<br />

java.sql.Connection 對象來執行查詢作業。這時你可以提供 connection 對象來作為通用數據源對象<br />

(usual data source object)。例子有:jasper,scriptlet,subreport 和 query。<br />

當然你可以在應用程式中即 JasperReport 之外執行 SQL 查詢。這様的話,你可以手動的外覆<br />

java.sql.ResultSet,再調用報表裝 填過程之前實例化這個數據源對象。當使用這種類型的數據源的時<br />

候,你需要為在 result set 中的每一列生命一個 report field。report field 的名字和類型必須和列的<br />

名字和類型符合。<br />

Class net.sf.jasper.engine.JREcptyDataSource<br />

這個類主要用于當生成報表的數據不是來自數據源,而是來自參數或重要的僅是數據源中 virtual<br />

records 的數量的時候。例子 fonts,images,shapes 和 unicode 都使用了這個類來裝填報表,來模擬數<br />

據源中没有一條記録,所有 欄位都為 null 的情况。<br />

Class net.sf.jasper.engine.data.JRTableModelDataSource<br />

這個 JRDataSource 介面的預設實現外覆了 javax.swing.table.TableModel 對象,它可以用在 Java<br />

Swing 應用程式中從已經顯示到螢幕上的 table 中得數據來生成報表。--我喜歡。<br />

通常有兩種方法來使用這種數據源:<br />

通常,為了要從中取得數據,你需要為 javax.swing.table.TableModel 對象的每一列生命一個 report<br />

field。但是有些情况下會出現問題,比如 report field 的命名需要遵照 Java 命名規範來聲明變量,而<br />

table 的列名則不需要。幸運的是,你仍然可以通過列的索引而不是它的名字來將 report field 與列進<br />

行對映。例如,一個列名為“Produce Description”不可能被對映到名為“Produce Description”的<br />

report field 上,因為 report field 名中含有白字元,這將引起一個編譯錯誤。但是如果你知道這個列<br />

示 table model 對象的第三列(index=2),那麽你就可以命名相應的欄位“COLUMN_2”并無誤地使用這<br />

一列的數據。例子有:datasource<br />

Class net.sf.jasper.engine.data.JRBeanArrayDataSource<br />

這個類外覆了一個 JavaBeans 數組,并且通過反射來獲取 report field 的值。在這種數據源中,一個<br />

JavaBean 對象說明了一條記録。如果我們有一個名為“ProductDescription”的 report field,在獲取<br />

這個欄位的值的時候,程式將會試圖通過反射機制調用一個當前 JavaBeans 對象中]名為<br />

getProductDescription()的方法。對于 boolean 欄位,當調用 get 前綴的屬性無法傳回其屬性值的時候,<br />

程式將會試圖使用 is 前綴的方法來獲得屬性值。<br />

Class net.sf.jasper.engine.data.JRBeanCollectionDataSource<br />

這個類和上一個類非常類似,它也是使用反射機制和 JavaBean 命名規範,但是它外覆了一個<br />

java.util.Collection 對象而不是一個 JavaBean 對象數組。在 datasource 例子中你可以看到進一步的<br />

用法。<br />

5.4 報表查詢(Report Query)<br />

為了要為報表裝填數據,我們需要為報表引擎提供所需的數據,或者至少告訴它怎様去獲取數據。<br />

JasperReport 通常需要接受一個 net.sf.jasper.engine.JRDataSource 對象作為報表的數據源,同時作<br />

為更為强大的功能,JasperReport 能直接用 JDBC 從關系數據庫總獲取數據。類庫允許用户在他們的報<br />

表設計中提供 SQL 查詢以便可以自運行期從數據庫中擷取數據。要做到這一點,你只需要在裝填的時 候<br />

為裝填管理者的 fillReport()方法提供一個 java.sql.Connection 而不是 JRDataSource 對象即可。<br />

在報表中,可以使用元素來引入查詢。如果這個元素存在,則出現在報表參數聲明之後,報表 field 之<br />

前。


如下是一個 SQL 查詢的例子:<br />

為了更好的自訂從數據庫中取回的數據集(data set),一個重要的方面是在報表查詢字符串中報表參<br />

數的使用(use of report parameters)。在查詢中,這些參數可能會像動態過濾器(dynamic<br />

filter)一様工作,它們用特殊的語法被引入進來為報表提供數據,很像 report expression。<br />

如下有兩種在查詢中的使用參數的方法:<br />

1. 像通常的 java.sql.PreparedStatement 的參數那様使用,用如下語法:<br />

SELECT * FROM Orders WHERE OrderID <br />

2. 有時,我們需要使用參數來動態變更 SQL 查詢的某些部分,或將整個 SQL 查詢作為參數提供給裝填過<br />

程。在這種情况下,語法稍微有些不同,向下面的例子,注意“!”<br />

SELECT * FROM $P!{MyTable} ORDER BY $P!{OrderByClause}<br />

]]><br />

在這個例子中,這個引入了參數值得特殊的語法確定了我們為這些參數所提供的值將會交替查詢中的參<br />

數參照($P!{}的内容)。這些參數將被傳給使用 java.sql.PrepqredStatement 對象的數據庫服務器。<br />

事實上,報表引擎首先處理$P!{}參數參照,通過使用他們的值來獲取最重的 SQL 查詢,并且僅當這件事<br />

完成之後,引擎才會將剩餘的普通的$P{}參數參照傳遞給 usual IN parameters。--實際上就是嵌套查詢<br />

啦。<br />

第二種用于 SQL 查詢的參數參照允許你在運行期傳遞整個 SQL 查詢語句:<br />

$P!{MySQLQuery}<br />

注意:你無法在參數值中再加入參數參照,也就是説,參數參照無法嵌套使用。<br />

更詳細的資訊可以參看工程所帶的例程:jasper,subreport,scriptlet,webapp 以及最有學習價值的<br />

query

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!