1. 簡介
JasperReport是一個強大的開源報表工具,它可以傳送豐富的報表內容到顯示器、打印機或者PDF、HTML、XLS、CSV、XML文件。它完全使用Java編寫,可以在各種Java應用中用來創建動態報表內容。它的主要目標是用簡單靈活的方法幫助創建便于打印的分頁文檔。
JasperReport根據一個xml報表設計文件來組織從JDBC獲得的關系數據庫數據。要用數據填充報表,首先必須編譯報表。編譯xml的報表設計文件是用JasperManager類的compileReport()方法完成的。
通過編譯,報表設計被加載到一個報表設計對象(net.sf.jasperreports.engine.JasperReport類的實例)中并被序列化然后保存。在應用程序用數據填充報表時使用該序列化文件。實際上,報表編譯完成了報表設計中所有的java表達式的編譯。很多檢查工作在編譯期間進行以確保報表設計的完整性,編譯后的文件是待填充的報表,以方便應用程序用各種數據集來產生不同的報表文檔。
要填充報表,可以使用JasperManager類的fillReportXXX()方法。這些方法接受一個參數代表報表設計——可以是一個JasperDesign對象,也可以是一個存放該類對象的文件名——還有一個獲得填充報表數據的JDBC連接。報表填充的結果是一個表示待打印文檔的對象(net.sf.jasperreports.engine.JasperPrint類的實例),可以被序列化保存以后繼續使用,或者傳送給打印機、顯示器,或者導出成PDF、HTML、XLS、CSV或者XML文件。
2. 報表設計
一個報表設計表示一個模版用來被JasperReport引擎填充數據并傳送到屏幕、打印機或者Web。數據庫的數據根據報表設計被組織來填充報表以得到待打印的分頁文檔。報表設計都保存到一個特定結構的一個XML文件中,文件結構定義在一個JasperReport引擎可以識別的DTD文件中。然后這些xml文件會被編譯以準備報表填充操作。
創建一個報表設計(模版),必須按照如下結構編輯一個xml文件:
<?xml version="1.0"?>
<!DOCTYPE jasperReport
PUBLIC "-//JasperReports//DTD Report Design//EN"
"http://jasperreports.sourceforge.net/dtds/jasperreport.dtd">
<jasperReport name="name_of_the_report" ... >
...
</jasperReport>
3. 報表參數
報表參數是傳遞給報表填充操作的對象的引用,為報表引擎傳遞它無法在數據源中找到的數據是非常有用的。例如,我們可以將登陸執行報表填充操作的用戶名傳給引擎,這樣我們可以在報表上顯示制表人或者動態改變報表的標題。
一個使用報表參數的重要作用是完成報表的動態查詢語句,以使報表獲得的數據更加符合要求,這些參數就像報表數據的過濾器。
在報表中聲明參數非常簡單,只需要指定名稱和類型(java類):
<parameter name="ReportTitle" class="java.lang.String"/>
<parameter name="MaxOrderID" class="java.lang.Integer"/>
<parameter name="SummaryImage" class="java.awt.Image"/>
可以用兩種方法在查詢語句中使用報表參數:
1. 就像通常在java.sql.PreparedStatement中使用參數一樣:
SELECT * FROM Orders WHERE OrderID <= $P{MaxOrderID} ORDER BY ShipCountry
2. 有時需要用參數來動態改變SQL查詢的部分語句或者將整個SQL語句作為參數傳給報表,在這種情況下,語法有一點不同,如下:
SELECT * FROM Orders ORDER BY $P!{OrderByClause}
還有一些報表內建的系統參數可以直接在表達式中使用:
REPORT_PARAMETERS_MAP
REPORT_CONNECTION
REPORT_DATA_SOURCE
REPORT_SCRIPTLET
4. 數據源
JasperReport只是各種類型的數據源,并提供一個JRDataSource的接口。該有一個缺省的實現類(JRResultSetDataSource class)包裝了ResultSet對象,允許使用任何通過JDBC連接的數據庫。使用JDBC數據源時,即可以通過將數據庫連接傳給報表填充引擎并在報表定義中指定一個SQL查詢語句(參考dtd定義中的<queryString>元素)來提供數據,也可以直接用ResultSet作參數生成JRResultSetDataSource對象來提供數據。
對于其他的數據源,也不會太麻煩,只需要實現JRDataSource接口來創建自己的數據源類。
5. 字段
報表字段提供了唯一映射數據源中數據到報表數據的方式。如果數據源是ResultSet對象,報表字段必須對應ResultSet對象中的列,就是說報表字段必須和對應的列有相同的名字和匹配的類型。
例如,我們要創建的報表需要用Employees表的數據,該表結構如下:
Column Name Datatype Length
--------------------------------------
EmployeeID int 4
LastName varchar 20
FirstName varchar 10
HireDate datetime 8
我們可以在報表設計文件中定義如下的字段:
<field name="EmployeeID" class="java.lang.Integer"/>
<field name="LastName" class="java.lang.String"/>
<field name="FirstName" class="java.lang.String"/>
<field name="HireDate" class="java.util.Date"/>
如果我們生命一個報表字段在ResultSet中沒有對應的列,則會在運行時拋出異常。當然ResultSet中的列沒有被聲明為報表字段不會影響報表的數據填充,但是他們仍然是可以訪問的。
6. 表達式
表達式是JasperReport的一個很強大有用的特性。用表達式可以:聲明報表變量來完成各種計算,為數據分組,指定報表文本字段內容或對其他報表對象的顯示進行更靈活的定制。基本上,所有的報表表達式都是Java表達式,并且可以引用報表字段和報表變量。
在報表設計的xml文件中有諸多定義表達式的元素:<variableExpression>, <initialValueExpression>, <groupExpression>, <printWhenExpression>, <imageExpression> 和<textFieldExpression>。
要在在表達式中引用報表字段,字段名必須寫在$F{和}符號之間。例如,如果我們要在一個文本域中連接兩個字段,我們可以像下面定義表達式:
<textFieldExpression>
$F{FirstName} + " " + $F{LastName}
</textFieldExpression>
表達式可以更復雜:
<textFieldExpression>
$F{FirstName} + " " + $F{LastName} + " was hired on " +
(new SimpleDateFormat("MM/dd/yyyy")).format($F{HireDate}) + "."
</textFieldExpression>
要在表達式中引用一個變量,必須將變量名寫在$V{和}符號之間,如下:
<textFieldExpression>
"Total quantity : " + $V{QuantitySum} + " kg."
</textFieldExpression>
對于報表參數也是同樣的語法,只不過參數名必須寫在$P{和}符號之間:
<textFieldExpression>
"Max Order ID is : " + $P{MaxOrderID}
</textFieldExpression>
7. 變量
報表變量是在表達式之前構建的專用對象。變量只聲明一次,而可以在整個報表設計中重復使用,并在對應的表達式中完成大量的計算,從而簡化了報表設計。在表達式中,一個變量可以引用其它變量,但是被引用變量必須在引用變量之前聲明。所以變量的聲明順序對報表設計也是很重要的。
變量還可以聲明來完成引擎內建計算的求值,如:count、sum、average、lowest、highest、variance等等。一個完成Quantity字段sum計算的變量定義如下:
<variable name="QuantitySum"
class="java.lang.Double" calculation="Sum">
<variableExpression>$F{Quantity}</variableExpression>
</variable>
我們還可以通過制定初始化級別來改變計算過程,默認的級別是Report就是變量僅在報表開始處初始化一次,一直到報表結束完成計算。我們可以選擇更低的級別讓變量在每個Page、Column或者Group級別重新初始化。假如我們想計算計算每頁的總數,變量聲明如下:
<variable name="QuantitySum" class="java.lang.Double"
resetType="Page" calculation="Sum">
<variableExpression>$F{Quantity}</variableExpression>
<initialValueExpression>new Double(0) </initialValueExpression>
</variable>
變量將在每一頁的開始處被初始化為0。
引擎還提供了如下的內建變量可以在表達式中直接使用:
PAGE_NUMBER
COLUMN_NUMBER
REPORT_COUNT
PAGE_COUNT
COLUMN_COUNT
GroupName_COUNT
8. 報表區域
在創建報表模板時,我們需要定義報表區域的內容和風格。一個完全的報表模板包括如下幾個區域:<title>, <pageHeader>, <columnHeader>, <groupHeader>, <detail>, <groupFooter>, <columnFoter>, <pageFooter>, <summary>。區域是報表的重要組成部分,它有指定的高度和寬度,并且可以容納直線、矩形、圖片或者文本域等報表對象。我們用<band>標簽在報表模板xml文件中定義報表區域的內容和風格。下面是一個PageHeader區域的定義,它僅僅包含一條直線和一個靜態文本:
<pageHeader>
<band height="30">
<rectangle>
<reportElement x="0" y="0" width="555" height="25"/>
<graphicElement/>
</rectangle>
<staticText>
<reportElement x="0" y="0" width="555" height="25"/>
<textElement textAlignment="Center">
<font fontName="Helvetica" size="18"/>
</textElement>
<text>Northwind Order List</text>
</staticText>
</band>
</pageHeader>
9. 分組
組表示一種分組組織數據的方式。填充報表數據時,JasperReport引擎計算所有定義的分組表達式檢查是否出現組邊界(表達式的值改變),如果遇到組邊界則將<groupFooter>和<groupHeader>報表區域加入報表。
報表可以包含任意多的分組,組在報表中的聲明順序很重要,因為組之間相互包含。一個組包含其后聲明組依此類推,一個大的組遇到邊界,所有的子組都將被重新初始化。一個報表組跟其數據分組表達式一起定義,同時還需要定義兩個報表分組區域:分組頭區域和分組尾區域。
關于分組的詳細信息參考分組的報表示例。
10. 字體和Unicode支持
現在你可以用任何語言來創建報表。<font>元素的新屬性允許在Java字體和PDF字體間映射。PDF使用特定的字體集使得以前的JasperReport版本沒有辦法使用它們。新的屬性使用戶可以指定什么PDF字體用來顯示不同的字符集(pdfFontName屬性),什么編碼類型(pdfEncoding屬性)和是否將字體嵌入PDF文檔(isPdfEmbedded)。
為了簡化字體集的使用,增加了一個新屬性<reportFont>。報表字體是報表級別的字體定義用來作為報表中其他顯示對象的默認字體。因為對國際字符集的支持不知為何被綁定到iText庫,你可以在iText documentation.文當中找到更多關于如何用不同的語言不同的字符集創建PDF文檔的信息。
11. Scriptlets
所有的報表顯示數據來自報表變量和報表字段,這些數據可以用報表變量和表達式來處理。
有時候報表需要對變量進行特殊處理,一些變量可能在報表的某個事件中(報表開始、換頁或者換列)被重新初始化,而且,變量在每次從數據源中獲得數據時(每一行)都被計算。而僅僅用簡單變量表達式無法實現所有的復雜功能,這時就要使用Scriptlet。
因為Scriptlet主要和報表變量一起工作,完全控制scriptlet的執行時機非常重要。JasperReport允許根據報表事件定制Java編碼BEFORE或者AFTER:Report、Page、Column和Group的初始化來執行Scriptlet。
要使用Scriptlet,開發者只需要通過繼承net.sf.jasperreports.engine.JRAbstractScriptlet或者net.sf.jasperreports.engine.JRDefaultScriptlet來創建Scritplet類。該定制的Scriptlet類會被指定為<jasperReport>的scritpletClass屬性的值。創建Scriptlet時開發這需要實現或者重載如beforeReportInit(), afterReportInit(), beforePageInit(), afterPageInit(), beforeGroupInit(), afterGroupInit(),等方法。這些方法將在填充數據時被引擎在適當的時候調用。
有一個叫做REPORT_SCRIPTLET的默認報表參數表示對報表引擎在填充數據時實例化的Scriptlet對象的引用。它可以在整個報表的任何表達式中使用來調用Scriptlet的特定方法使整個報表機制更加靈活。
12. 子報表
子報表是報表工具的重要特性,它允許創建更復雜的報表并簡化設計工作。自報表在創建主從報表時特別有用。