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. 子报表
子报表是报表工具的重要特性,它允许创建更复杂的报表并简化设计工作。自报表在创建主从报表时特别有用。