天天看点

【译】JasperReports Library使用指南

简介(introduction)

        jasperreports是一个强大的开源报表工具,它能够传送丰富的内容到屏幕上、打印机或者转换为pdf、html、xls、rtf、odt、csv、txt和xml文件等多种格式的文件。jasperreports完全由java编写,它可以被用于各种各样的java程序来动态的生成内容。它主要的目的就是帮助以一种简单且灵活的方式生成基于页面的、可打印的文档。

jasperreports会整理从jrxml文件(报表设计文件,又称为报表模版文件,后缀为*.jrxml)中定义的数据源中取出的数据。为了给一个报表填充数据,报表设计文件必须首先经过编译。

如上所述,使用jasperreports时用到的主要的类有:

<a href="http://jasperreports.sourceforge.net/api/net/sf/jasperreports/engine/jaspercompilemanager.html">net.sf.jasperreports.engine.jaspercompilemanager</a>

<a href="http://jasperreports.sourceforge.net/api/net/sf/jasperreports/engine/jasperfillmanager.html">net.sf.jasperreports.engine.jasperfillmanager</a>

<a href="http://jasperreports.sourceforge.net/api/net/sf/jasperreports/engine/jasperprintmanager.html">net.sf.jasperreports.engine.jasperprintmanager</a>

<a href="http://jasperreports.sourceforge.net/api/net/sf/jasperreports/engine/jasperexportmanager.html">net.sf.jasperreports.engine.jasperexportmanager</a>

这些类代表了jasperreports引擎的门面类,它们包含了一系列简化了访问api的静态方法,它们可用于编译一份报表设计文件、为报表填充数据、打印报表或者将结果导出到其他文档格式。

为了创建一个简单的报表设计,我们需要以以下结构来编辑一份xml文件:

<a href="http://my.oschina.net/itblog/blog/287691#">?</a>

1

2

3

4

5

6

7

<code>&lt;!--?xml version="1.0"?--&gt;</code>

<code>&lt;</code><code>jasperreport</code> <code>...</code><code>=</code><code>""</code> <code>name</code><code>=</code><code>"name_of_the_report"</code>

<code>                     </code><code>xmlns</code><code>=</code><code>"http://jasperreports.sourceforge.net/jasperreports"</code>

<code>                     </code><code>xmlns:xsi</code><code>=</code><code>"http://www.w3.org/2001/xmlschema-instance"</code>

<code>                     </code><code>xsi:schemalocation</code><code>=</code><code>"http://jasperreports.sourceforge.net/jasperreports http://jasperreports.sourceforge.net/xsd/jasperreport.xsd"</code><code>&gt;</code>

<code>    </code><code>...</code>

<code>&lt;/</code><code>jasperreport</code><code>&gt;</code>

为了更好的理解jrxml文件的结构,或者一份报表设计的流程,我们推荐你看看快速引用(原链接已失效)。

编译一个报表设计文件时,引擎首先会执行一步验证来确保设计文件的一致性,然后

转换所有报表中的表达式为一个待赋值的形式,并且将它们存储在编译后的jasperreport文件或者*.jasper文件中。

这个转换要么是跟报表模版相关的java类的快速编译过程,要么是生成将要在报表填充阶段计算表达式值时使用的groovy或者beanshell脚本,这取决于报表中指定的表达式的语言(详见报表模版文件的language属性)。

在更深入了解报表编译之前,你应该通过阅读以下常见问题来了解什么时候你需要编译模版文件,以及怎样才能以最好的方式编译它们:

什么情况下我应该编译我的模版文件?怎样编译?(原链接已失效)

        1. java报表编译器。这个编译器生成并编译一个包含计算表达式值的方法的java类。

用ant任务来编译报表模版文件

模版文件的编译任务可以通过在工程的build.xml文件中像下面这样声明:

<code>&lt;</code><code>taskdef</code> <code>classname</code><code>=</code><code>"net.sf.jasperreports.ant.jrantcompiletask"</code> <code>name</code><code>=</code><code>"jrc"</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>classpath</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>fileset</code> <code>dir</code><code>=</code><code>"./lib"</code><code>&gt;</code>

<code>            </code><code>&lt;</code><code>include</code> <code>name</code><code>=</code><code>"**/*.jar"</code> <code>/&gt;</code>

<code>        </code><code>&lt;/</code><code>fileset</code><code>&gt;</code>

<code>    </code><code>&lt;/</code><code>classpath</code><code>&gt;</code>

<code>&lt;/</code><code>taskdef</code><code>&gt;</code>

在上面的例子中,lib文件夹应包含jasperreports-&lt;version&gt;.jar文件和它所依赖的库文件(包含jdt-compiler-&lt;version&gt;.jar,万一你使用java作为报表表达式的语言,这是推荐的报表编译器)。

接下来这个用户自定义的ant任务可以用于在一个单个操作中编译多个jrxml报表模版文件。可以通过指定包含这些jrxml文件的根目录或者通过使用文件的模式匹配选择jrxml文件来指定它们。下面是模版文件编译任务的一些属性:

属性

描述

srcdir

用于编译的jrxml文件的位置。 除非相关的元素会呈现,否则是不可省(required)的。

destdir

用于存放编译后文件的位置(默认与srcdir相同)

compiler

(可选)实现了jrcompiler接口的类的名字

xmlvalidation

用于标记报表模版的源文件的xml验证是否将被执行的标记(默认值为true)

tempdir

用于存储临时生成文件的位置(默认值为当前工作目录)

keepjava

用于表明临时生成的java文件是否需要保留的标记(默认值为false)

像ant内置任务&lt;javac&gt;一样,报表模版文件编译任务支持内嵌的&lt;src&gt;和&lt;classpath&gt;元素。为了看到这一点起作用,可以查看jasperreports项目源码中提供的demo/samples/antcompile例子

 查看一个报表设计文件

这是因为大多数时候,我们使用一个jdbc连接获取关系型数据库中的数据来填充报表,我们可以很方便的在报表模版文件中使用sql查询。jasperreports引擎可以使用传递给它的connection执行sql查询,由此生成一个报表数据源用于填充报表。

如果数据以其他的形式提供,此时将用到接收一个数据源作为参数的方法。

参数是在报表填充操作中传入的对象的引用。它在传递不能从数据源中找到的数据给报表引擎时非常有用。例如:如果我们需要在报表中显示启动报表填充操作的用户的名字,我们可以传递一个用户名给引擎;或者我们可以通过参数来动态的改变报表的标题。

还有一个很重要的方面是:为了能在更进一步的自定义从数据库中取到的数据,可以在报表的查询字符串中使用参数,这些参数可以像为报表提供数据的查询中的动态过滤器一样工作。

在报表模版中声明一个参数很简单,我们只需要指定其类型和参数名,例如:

<code>&lt;</code><code>parameter</code> <code>class</code><code>=</code><code>"java.lang.string"</code> <code>name</code><code>=</code><code>"reporttitle"</code> <code>/&gt;</code>

<code>&lt;</code><code>parameter</code> <code>class</code><code>=</code><code>"java.lang.integer"</code> <code>name</code><code>=</code><code>"maxorderid"</code> <code>/&gt;</code>

<code>&lt;</code><code>parameter</code> <code>class</code><code>=</code><code>"java.awt.image"</code> <code>name</code><code>=</code><code>"summaryimage"</code>  <code>/&gt;</code>

在查询中有两种可能使用这些参数的方式:

1. 参数用于普通的java.sql.preparedstatement的参数,使用以下格式:

<code>select</code> <code>* </code><code>from</code> <code>orders </code><code>where</code> <code>customerid = $p{ordercustomer}</code>

2. 有时,使用参数来动态修改sql查询的一部分或者传递整个sql查询作为参数来填充报表是很有用的,在这些情况下,格式有些不同,例如下面的例子:

<code>select</code> <code>* </code><code>from</code> <code>orders </code><code>order</code> <code>by</code> <code>$p!{orderbyclause}</code>

下面还有些系统中内置的参数,在表达式中可以直接使用:

参数名

report_parameters_map

这个参数包含了所有用户定义和内置的参数的map

report_connection

一个用户提供的,用于jdbc连接数据源的java.sql.connection

report_data_source

一个代表系统内置的数据源类型或者用户提供的数据源的jrdatasource

report_max_count

一个用于允许用户限制数据源大小的整数

report_scriptlet

report_locale

一个包含资源组期望的locale实例的java.util.locale对象

report_resource_bundle

包含了本地化消息的java.util.resourcebundle对象

report_time_zone

一个用于为用户格式化日期的java.util.timezone实例

report_virtualizer

report_class_loader

一个在报表填充阶段用到的用于加载像images、fonts、或者subreport模版的java.lang.classloader 实例

is_ignore_pagination

如果被设置成java.lang.boolean.true,报表将会生成一个很长的页面,不会插入分页符

使用一个jdbc数据源时,你需要传递一个java.sql.connection对象给报表填充操作,并指定报表定义中的query(可以查看xml文件中的元素);或者通过直接提供一个java.sql.resultset对象来创建一个新的jrresultsetdatasource实例。

报表的字段是唯一映射数据源中的数据到报表生成子程序的方式。当报表的数据源是一个java.sql.resultset时,java.sql.resultset对象中的所有的字段必须映射给相应的列,这些列必须拥有和resultset字段同样的名字和相兼容的数据类型。

比如:如果我们想用employees表中取出的数据生成一个报表,该表为以下结构:

字段名

数据类型

长度

employeeid

int 

lastname

varchar

20

firstname

10

hiredate

datetime

8

我们可以在报表模版文件中定义以下字段:

<code>&lt;</code><code>field</code> <code>class</code><code>=</code><code>"java.lang.integer"</code> <code>name</code><code>=</code><code>"employeeid"</code> <code>/&gt;</code>

<code>&lt;</code><code>field</code> <code>class</code><code>=</code><code>"java.lang.string"</code>  <code>name</code><code>=</code><code>"lastname"</code>   <code>/&gt;</code>

<code>&lt;</code><code>field</code> <code>class</code><code>=</code><code>"java.lang.string"</code>  <code>name</code><code>=</code><code>"firstname"</code>  <code>/&gt;</code>

<code>&lt;</code><code>field</code> <code>class</code><code>=</code><code>"java.util.date"</code>    <code>name</code><code>=</code><code>"hiredate"</code>   <code>/&gt;</code>

如果我们声明一个字段,该字段在java.sql.resultset中没有相应的列对应,那么将会在运行时抛出一个异常。一个在java.sql.resultset中的列的对象如果在报表中没有对应的字段,那么在报表填充操作时将不受影响,但是它同样不可访问。

表达式是jasperreports的一个强大的功能。它们可用于声明报表中对数据分组时将会执行一系列计算的变量,来指定报表文本域内容甚至自定义报表中对象的样式。

基本上所有的报表表达式是java表达式,它们可以引用报表中的字段(fields)和变量(variables)。

在一个xml报表模版文件中,定义的表达式有以下几种:

&lt;variableexpression&gt;

&lt;initialvalueexpression&gt;

&lt;groupexpression&gt;

&lt;printwhenexpression&gt;

&lt;imageexpression&gt;

&lt;textfieldexpression&gt;

在一个表达式中使用一个报表字段(field),字段名必须放在”$f{“和”}”之间,比如:如果我们想在报表中的一个textfield中显示两个字段连接后的值,我们可以像下面这样定义一个表达式:

<code>&lt;</code><code>textfieldexpression</code><code>&gt;</code>

<code>    </code><code>$f{firstname} + " " + $f{lastname}</code>

<code>&lt;/</code><code>textfieldexpression</code><code>&gt;</code>

表达式甚至可以更复杂一些:

<code>    </code><code>$f{firstname} + " " + $f{lastname} + " was hired on "</code>

<code>    </code><code>+ (new simpledateformat("mm/dd/yyyy")).format($f{hiredate}) + "."</code>

在表达式中引用一个变量(variable),变量名必须放在”$v{”和”}”之间,像下面的例子一样:

<code>    </code><code>"total quantity : " + $v{quantitysum} + " kg." </code>

在表达式中,可以对参数(parameter)使用相同的符号。参数名应该放在”${”和”}”之间,像下面的例子一样:

<code>    </code><code>"max order id is : " + $p{maxorderid} </code>

报表的变量是一个在表达式基础上特殊的对象。变量可以通过仅仅被声明一次并在报表中用于多次,或者在相应的表达式中进行一系列的计算来简化报表设计。在它的表达式中,一个变量可以引用其他变量,但前提是这些被引用的变量之前报表中已经有声明。因此,报表中变量声明的顺序很重要。

正如提到的,变量可以在他们相应的表达式中执行内置类型的计算,例如count,sum,average,lowest,highest等。

一个用于计算数量总和的变量应该像以下这样声明:

<code>&lt;</code><code>variable</code> <code>calculation</code><code>=</code><code>"sum"</code> <code>class</code><code>=</code><code>"java.lang.double"</code> <code>name</code><code>=</code><code>"quantitysum"</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>variableexpression</code><code>&gt;</code>

<code>        </code><code>$f{quantity}</code>

<code>    </code><code>&lt;/</code><code>variableexpression</code><code>&gt; </code>

<code>&lt;/</code><code>variable</code><code>&gt;</code>

对于执行计算的变量,我们可以指定它们重新初始化的级别。默认级别是report,那意味着这个变量在整个报表开始时只初始化一次,然后它将执行指定的计算直到报表结束。但是我们可以指定一个重置变量的较低级别,例如页面级别(page),列级别(column)或者组级别(group)。

比如:如果我们想计算每一页的总量,我们应该这样声明一个变量:

<code>&lt;</code><code>variable</code> <code>calculation</code><code>=</code><code>"sum"</code> <code>class</code><code>=</code><code>"java.lang.double"</code> <code>name</code><code>=</code><code>"quantitysum"</code> <code>resettype</code><code>=</code><code>"page"</code> <code>&gt; </code>

<code>    </code><code>&lt;/</code><code>variableexpression</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>initialvalueexpression</code><code>&gt;</code>

<code>        </code><code>new double(0)</code>

<code>    </code><code>&lt;/</code><code>initialvalueexpression</code><code>&gt;</code>

这个变量将在每一页开始时初始化为0值。

这里还有以下可以在表达式中直接使用的内置变量:

page_number

column_number

report_count

page_count

column_count

groupname_count

当构建一个报表时,我们需要定义区域的布局和内容。整个报表文件结构是基于以下区域的:

&lt;pageheader&gt;

&lt;groupheadr&gt;

&lt;groupfooter&gt;

&lt;pagefooter&gt;

&lt;summary&gt;

&lt;columnheader&gt;

&lt;detail&gt;

&lt;columnfooter&gt;

&lt;lastpagefooter&gt;

&lt;nodata&gt;

sections是报表中的一部分,它有一个指定的高度和宽度,并且可以包含例如线条(lines)、矩形(rectangles)、图像(images)或者文本域(textfields)等报表元素。当在xml报表模版文件中声明报表区域的布局和内容时,我们使用一般的元素。

下面这个页眉中,只包含了一个线条对象和一个静态文本:

9

11

12

13

<code>&lt;</code><code>pageheader</code><code>&gt;</code>

<code>    </code><code>&lt;</code><code>band</code> <code>height</code><code>=</code><code>"30"</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>rectangle</code><code>&gt;</code>

<code>            </code><code>&lt;</code><code>reportelement</code> <code>height</code><code>=</code><code>"25"</code> <code>width</code><code>=</code><code>"555"</code> <code>x</code><code>=</code><code>"0"</code> <code>y</code><code>=</code><code>"0"</code><code>&gt;&lt;/</code><code>reportelement</code><code>&gt;</code>

<code>            </code><code>&lt;</code><code>graphicelement</code><code>&gt;&lt;/</code><code>graphicelement</code><code>&gt;</code>

<code>        </code><code>&lt;/</code><code>rectangle</code><code>&gt;</code>

<code>        </code><code>&lt;</code><code>statictext</code><code>&gt;</code>

<code>            </code><code>&lt;</code><code>textelement</code> <code>textalignment</code><code>=</code><code>"center"</code><code>&gt;&lt;/</code><code>textelement</code><code>&gt;</code>

<code>            </code><code>&lt;</code><code>text</code><code>&gt;northwind order list&lt;/</code><code>text</code><code>&gt;</code>

<code>        </code><code>&lt;/</code><code>statictext</code><code>&gt;</code>

<code>    </code><code>&lt;/</code><code>band</code><code>&gt;</code>

<code>&lt;/</code><code>pageheader</code><code>&gt;</code>

有时候一组元素需要共享一个公共的背景色或者一个公共的边框。这是通过在它们后面放一个矩形来实现的,但是这样做对网格导出器(grid exporters)是不起作用的,因为它不支持重叠的元素。

新的frame元素将被网格导出器识别,可以被用于将元素嵌入frame中来对元素进行分组。frames可以被嵌入无限层。

分组代表了一种在报表中组织数据的灵活的方式。当填充一个报表的时候,jasperreports引擎将检测所有定义的分组表达式来判断是否发生了分组结束,如果是,引擎将在报表中使用相应的&lt;groupfooter&gt;(这个分组的groupfooter)和&lt;groupheader&gt;(下个分组的groupheader,如果有的话。译者注。)区域。

只要我们需要,在报表中我们可以有很多个group。报表中声明的分组顺序很重要,因为分组互相包含,一个组包含接下来的一个组,等等。当一个大的分组结束时,所有子分组会被重新初始化。

声明一个报表分组时,会同时给出相应的数据分组表达式,我们会声明两种区域:就是分组的header和分组的footer。

现在你可以以任何语言创建报表了!

介绍的元素中的新属性允许在java字体和pdf字体间建立映射关系。pdf使用特殊的字体设置,在jasperreports以前的版本中没有一种方式可以使用它们。通过介绍这些新的属性,用户可以指定pdf将使用哪种字体来显示不同的字符集(pdffontname属性),将使用何种编码方式(pdfencoding属性)以及是否该字体会被嵌入到pdf文档中(ispdfembedded属性)。

为了简化字体设置,一个新的元素出现了:

为了将一组被用于报表元素的视觉特性进行分组,引入了报表样式。这个新概念是一个以前的报表字体(现在已过时)的扩展。引用了报表样式的报表元素能够覆盖任何在样式中定义的视觉特性。

报表样式同样可以引用其他的报表样式,并且拥有同样的用于样式的继承和覆盖机制。样式在整个组的元素需要共享同样的视觉特性的时候非常有用,因为对样式的任何改变都会被用于该组中所有的元素。这可以通过改变它们共同引用的报表样式来实现。

在报表样式中显示的所有数据均来自报表参数(parameters)和报表的字段(fields)。这些数据可以使用报表变量(variable)和它们组成的表达式来处理。

处理变量有特定的时刻。一些变量在报表开始时根据它们的resettype来初始化,一些在遇上分页(page break)或者分列(column break)时,亦或者在分组改变(group change)时。另外,变量在每次从数据源中取出新数据时被赋值(每行都会赋值一次)。

但是只有简单的变量表达式有时不能实现复杂的功能,这时就要用到小脚本了。

小脚本是java代码片段,它们可以在每次报表事件发生时被执行。通过使用小脚本,用户可以修改报表变量中存放的值。

因为小脚本主要和报表变量一起工作,在准确的时刻完全控制脚本的执行是极为重要的。jasperreports允许用户自定义的java代码在根据变量的重置类型(report、page、column和group)初始化报表变量的“之前”或者“之后”被执行。

当创建一个jasperreports脚本类时,开发者需要实现或者覆盖几个方法,比如:beforereportinit, afterreportinit(),  beforepageinit(), afterpageinit(), beforegroupinit(), aftergroupinit()等,这些方法将会在填充报表的合适时候被报表引擎调用。

有一个默认的报表参数叫report_scriptlet,它代表了一个对报表引擎在填充报表时初始化的脚本对象的引用,它可以被用于整个报表的表达式中来调用脚本对象中的自定义方法,这使得整个机制更加灵活。

子报表是一个报表生成工具重要的功能。子报表允许创建更加复杂的报表来简化设计工作。

子报表在创建概览--明细类型的报表或者单个报表不足以描述复杂的输出文档时非常有用。

子报表其实就是一个包含在另一个报表中的普通报表。一个报表可以和子报表重叠,还可以创建包含自己的一个子报表,这种嵌套可以深至无限的深度。任何的报表模版在被嵌入到其他报表时不需要修改里面的任何地方就可以被作为一个子报表。

一共有两种方式可以为子报表提供参数值。一种是,你可以使用用于生成指定参数映射map的元素,或者你可以使用&lt;subreportparameter&gt;元素为每一个相关的参数赋值。如果同时使用两种方式为子报表参数赋值,那么使用&lt;subreportparameter&gt;指定的参数值将覆盖使用&lt;parametersmapexpression&gt;元素指定的值。

就像普通报表一样,子报表也以同样的方式,需要一个数据源来生成相应的内容,并且子报表在填充时希望接收到相同的输入类型。

从子报表中计算出的值可以被返回到父报表,在一个子报表填充值之后,子报表的变量值可以被赋值或者累计(使用一个增长器)到父报表的变量中。

jasperreports允许通过使用新的resourcebundle属性在运行时或者在运行时为内置参数report_resource_bundle赋一个值来关联一个java.util.resourcebundle对象。

如果需要以不同于当前本地环境(locale)的环境生成,那么在填充报表时这个内置参数report_locale应该被用于指定运行时的本地环境。

为了更容易的国际化报表,在报表表达式中有一个特殊符号允许引用放在报表关联的java.util.resourcebundle对象中的字符串资源。$r{...}表达式用于封装资源束(resource bundle)的key来获取对应的值。

在生成的输出中,jasperreports库会保存文本运行方向,以便以不同语言生成的拥有从右到左的书写(比如阿拉伯语和希伯来语)的文档能够被正确的渲染。

图表和交叉表有时候需要使用在报表填充时没有被迭代的数据。这就是数据源的某些字段自身作为子报表的数据源这种情况。

为了避免使用子报表来渲染图表或者交叉表,我们引入了一个叫做数据集(dataset)的新概念。一个数据集是一个数据源和子报表之间的概念,因为它包含了参数(parameters)、字段(fields)、变量(variables)和分组(groups),但是没有布局(layout)信息。

jasperreports现在内置支持使用图表。尽管我们已经有图像、文本域子报表和其他元素可用,还有一种新的、立即可用的图表组件。这极大的简化了把图表嵌入到报表中的方式,因为这之前,用户必须完全依赖于小脚本(scriptlets)来收集图表数据并在报表模版中使用一个image元素来渲染图表。

现在通过使用新的chart组件,用户只需要设置期望的样式并且在迭代报表数据源时以一种增长的方式定义能够帮助引擎构建图表数据集的表达式即可。

在嵌入和配置图表组件时,有三个相关的实体:

全局的chart组件

chart数据集(将chart数据分组的相关设置)

chart样式(chart条目在渲染时相关的样式设置)

jasperreports目前支持以下几种图表:

饼图、3d饼图、条形图、3d条形图、堆积图、3d堆积图、折线图、区域图、散点图、气泡图、时序图和高低开合图。

这些图表使用几种数据集(每一种图表和几种指定的数据集工作):饼图数据集、种类数据集、xy数据集、时序数据集、时间段值、xyz数据集和高低数据集。

对于所有的图表,我们可以配置以下属性:

四周的边框

背景色

标题

标题位置(顶部、左边、底部、右边)

标题字体

标题颜色

子标题

子标题字体

子标题颜色

显示/隐藏图例

绘图区域背景色

绘图区域背景色透明度(alpha值)

绘图区域前景色透明度(alpha值)

绘图区域方向(垂直、水平)

坐标文本

对于所有的数据集,我们可以配置以下属性:

增长类型(详细、列、页、分组、报表)

增长分组

重置类型(无、按列、按页、按组、按报表)

重置分组

图表类型的详细设置:

3d饼图

深度因子

条形图、xy条形图、堆积条形图

隐藏/显示文本

隐藏/显示提示标记

隐藏/显示提示文本

3d条形图,3d堆积条形

图隐藏/显示文本

x偏移量(3d效果)

y偏移量(3d效果)

折线图、xy折线图、散点图、时序图

隐藏/显示线条

隐藏/显示图形气泡图

气泡图

缩放类型

高低开合图

隐藏/显示关闭提示

隐藏/显示打开提示

交叉表是一个特殊类型的表组件,它的行和列都是动态的。它们用于显示在列(columns)和组(groups)上使用多级分组的聚合数据。