iReport http://www.jasperreport.net
iReport是一个能够创建复杂报表的开源项目,它100%使用Java语言编写,是目前全球最为流行的开源报表设计器之一。
由于它丰富的图形界面,你能够很快的创建出任何一种你想要的报表。
以下是iReport一些比较突出的功能:
• 拖拽式,所见即所得的报表编辑器;
• 多样的向导来简化复杂的报表设计任务;
• 超过30个排版和格式化工具;
• 报表可转换为PDF HTML EXCEL FLASH CSV RTF TXT OpenOffice Java2D JRViewer等格式;
• 支持所有有效的数据源。如:JDBC,CVS,Hibernate,JavaBean等;
• 用户自定义数据源;
• 无限次数的撤消/重做;
• 集成脚本(scriptlet)支持;
• 内置图表支持:超过20种的图表支持。并可扩展;
• 国际化:集成超过15种语言;
• 报表模板与报表库样式管理;
• 源文件的备份;
• 文档结构浏览器。
在本章中我们将介绍iReport运行时所需要的环境、如何下载iReport以及如何在我们的系统中安装使用。
iReport运行时需要sun java2 SDK 1.5及以上版本,为了能编译我们的报表文件我们需要完整的安装JDK。
iReport官方网址:http://ireport.sourceforge.net/cap3.html。
将文件下载到本地解压后,您可以直接双击iReport.exe或者iReport.bat来运行程序。
注:在本文中iReport的版本是3.0.0。
在本章中我们将分析一下报表的结构,看一下报表中每一部分的作用是什么,以及在生成报表的时候每一部分将产生什么样的效果。
报表被垂直分成若干个部分,每一个部分我们叫它“band”。每一个band都有自己的特性,在报表生成的时候有些会打印一次,有些会打印多次。如下图。
接下来我们就对每一种类型的band分别进行介绍。
Title Band:title段只在整个报表的第一页的最上面部分显示,除了第一页,不管报表中有多少个页面也不会再出现Title band中的内容。就是报表的标题。
pageHeader Band:pageHeader段中的内容将会在整个报表中的每一个页面中都会出现,显示的位置在页面的上部。如果是报表的第一页,pageHeader中的内容将显示在Title Band 下面,除了第一页以外的其他所有页面中,pageHeader 中的内容将显示在页面的最上端,即页眉。
pageFooter Band:显示在所在页面的最下端,即页脚。
Detail Band:报表内容段,在这个Band中设计报表中需要重复出现的内容,Detail 段中的内容每页都会出现。
columnHeader Band:针对Detail Band的表头段,一般情况下在这个段中画报表的表头。
columnFooter Band:针对Detail Band的表尾段。
Summary Band:表格的合计段,出现在整个报表的最后一页中的Detail band的后面,一般用来统计报表中某一个或某几个字段的合计值。
lastPageFooter:内容将会出现在报表的最后一页的最后部分。
为了实际演示各个band的生成效果,我们可以在报表中的各个band里添加不同的对象,以测试各个band的作用。
步骤:
第一步:新建文档,报表名称命名为:MyFirstReport,然后单击“OK”。
第二步:设置JDBC连接参数
单击“Dateà连接/资料来源”,出现如下界面:
然后单击“New”,出现如下界面:
选择“Database JDBC connection”项,单击“Next”,
设置好参数,单击“Test”,如果测试成功单击“Save”保存,如果测试没有成功,请检查一下以上步骤。
第三步:单击Dataà报表查询。在query editor里输入下面的语句:
select * from tb_employee,然后单击“OK”
第四步:单击“不会变动的文字”按钮,添加各个Band内容。
在报表中的各个band分别放置如下内容:
第五步:将要显示的字段拖到“detail”栏内
第六步:单击“执行报表”查看结果:
启动报表,生成的效果如下:
最后一页:
在本章中我们将介绍可以在报表中使用的对象及他们的相关属性。
我们所说的元素主要是一些图形对象,比如text,rectangle等。在iReport中没有段落、表格或分页的概念,在报表中最基本的对象主要有七个:
-Line(线段)
-Rectangle(矩形)
-Ellipse(椭圆形)
-Static text(不会变动的文字)
-Text field(文字栏位)
-Image(图像)
-Subreport(子报表)
-Crosstab()
-Chart
通过以上这些组件的组合我们可以做出各种各样复杂的报表样式。iReport提供两种特殊的图形对象:chart和barcode。
每一种类型的对象都有一些通用的属性,如高度、宽度、位置、所在的band等。除此之外还有一些针对不同元素的特殊属性。
每一个元素都必须在一个band内部,如果一个元素跨了一个以上的band,那么在编译的时候将会抛出元素位置错误的异常。
要添加一个元素到报表中,我们可以从主工具栏中选择相应的工具,然后添加到报表区域中即可。
我们可以通过双击对象或右键菜单打开元素的属性设置窗口。
属性窗口由若干个标签页构成,“Common”标签里包含每一个组件都具有的通用属性,其它的标签页是针对不同元素的属性设置。在下面的章节中,我们将对每一个对象的使用做详细的介绍。
一个style是一个预定义的属性的集合,用来控制元素的外观(比如背景色、边框线、字体等)。我们可以为我们的报表定义一个默认的style。
要为一个元素应用一个style,我们可以选择该元素并从元素的属性窗口common标签里的style列表选择你需要的style。
Fonts是用来描述text的外观特征。
通常定义一个字体,我们需要做下面几件事情:
-Font name(字体名称)
-Font dimension(字体大小)
-Attribute(属性,诸如bold-faced,italics,underlined,barred)
如果我们需要导出PDF格式的报表,需要为字体添加下面的信息:
PDF Font Name:PDF字体名称(预先定义PDF字体或一个在classpath里的TIF文件的名称)。
PDF Embedded:当使用一个外部的TTF类别的字体文件生成报表时是否把它包含在PDF文件里的一个标志。
PDF Encoding:指定一个字体编码名称。
单击“格式化à报表字形”可以创建自定义的字体。
如果我们需要将报表导出成PDF格式,将会使用指定的PDF字体,其它属性将会被忽略掉。
我们可以选择主菜单的“Format Styles” 来定义一个style。如下图所示:
单击“格式化àStyles”可以创建自定义的样式。
点击“New”按钮添加一个新的style。在弹出的窗口中我们可以定义一些属性,如果我们需要某个属性的值,我们可以点击按钮来实现。
在每个元素属性窗口的“Common”标签里有一个style属性下拉框,如果我们需要为一个元素设置一个style,我们可以在这个下拉框里选择我们定义好的style,这样就可以把一个style应用到一个元素上面了。
我们可以为一个Style加一个动态的条件,当条件满足的时候采用某个style,否则就采用默认的style。举个例子,还是以数据库里的employee表为例,如果员工的工龄大于3时,我们让工龄以红色加粗的字体显示。
打开Query窗口,输入下面的查询SQL:
Select * from tb_employee
新建一个style,在弹出窗口的style conditions里新建一个conditions,在表达式窗口里输入下面的表达式:
($F{employee.workingYear}.intValue()>2?newBoolean(true):new Boolean(false))
如下图:
新建的style如下图:
接下来把员工姓名和工龄到报表当中,并将我们新建的style应用到薪水这个field上,如下图:
启动查看运行效果如下:
在ireport中有三种类型的对像可以用来保存数据:Fields/Parameters/Variables.这三种对象可以用在某些地方的Expression中,通过一定的逻辑在报表生成的时候动态的更改某些值。Fields/Parameters/Variables有类型的概念,它们的类型就是一个标准的Java的Object。要使用这三种类型的对象我们必须首先创建它,创建的方法是点击“View”主菜单中,选择其中的子菜单Fields,Variables,Parameters我们可以创建不同的对象。通过这三个子菜单我们可以查看并管理Fields,Variables,Parameters对象。
会出现如下界面:
通过该窗口,我们可以创建,修改,删除这三种类型对象。
Field在ireport是专门用来组织记录。Field的创建有多种途径可以实现,我们可以根据在ireport中各种类型的数据源来创建我们所需要的Field。
一个Field有两个必填的属性: name、type和一个可选项的description。从ireport1.0.0开始,fields可以是任何Java数据类型。这样当我们使用JavaBean作为数据源时我们可以很方便的从java.lang.Object向报表中的Field的转换工作。在对象的Expression里你可以使用下面的语法来引用一个field:
$F{field name}
例如,如果你想处理com.bstek.test.domain.Person Bean里的username 字段,可以这样去写:
((com.bstek.test.domain.Person)${username})
在ireport的设计中,使用SQL query的方式来创建或记录字段的是使用最广泛的也是最直接最简单的一种方式。做此操作我们需要首先打开ReportQueryDialog窗口(点击主工具栏的 按钮),ReportQueryDialog窗口出现后,我们可以输入合适的SQL检索并创建Fields。
在打开ReportQueryDialog窗口之前请先确认我们已经有一个数据源连接处于激活状态(激活方式请查看相关章节),插入一条查询语句,如:select * form employee
iReport将启动SQL查询引擎,根据输入的SQL将分析结果用fields的形式展示出来。在下面的窗口中选择你要的fields(可以多选)然后点击OK按钮, fields就被创建出来了。
在这个例子当中所有字段有多种类型,一般地说,fields的数据类型是构建在原始SQL 类型基础之上的,如String,Integer…
ireport的高级特性之一是它的数据源除了可以构建在SQL query基础之上外,还可以使用含有getter和setter方法的JavaBean(或者叫POJO)对象来创建。在这个例子中fields就是POJO类里的属性(或者是属性的属性,即子对象的属性)。
第一步:定义一个简单的Bean:
package com.asinfo.ccm.model;
public class ProductBean {
private StringproductName;
public StringgetProductName() {
returnproductName;
}
public voidsetProductName(String productName) {
this.productName= productName;
}
}
第二步:要在iReport中使用JavaBean作为数据源,首先要定义好Classpath,以使iReport能够找到我们定义的Bean,使用菜单“Options”-“Classpath”定义Classpath,定义的路径为编译后的.class文件所在路径,例如,我定义的Classpath为“C:\Documents and Settings\wzz\workspace\ccmrightinfo\WebRoot\WEB-INF\classes”。
第三步:在菜单“Data”-“Connections/Datasources”菜单中,添加数据源,将数据源类型设为“JavaBeans set data source”,设定的数据源属性如下图所示:
第四步:然后到菜单“Data”-“Report Query”中定义需要用到的 Bean属性,在“Class name”中输入自定义Bean的名称,本例中为“lld.test.ireport.ProductBean”,然后点击“Read attributes”按钮,获取Bean属性,点击“Add Selected Field(s)”添加要用的属性,如下图所示:
要把一个属性添加到field列表,只需选中需要的属性然后点击“Add field(s)”按钮。
Parameters通常是用来在打印的时候从程序里传值到报表里。也就是说parameters通常的是起参数传递的作用。他们可以被用在一些特定的场合(比如应用中SQL 查询的条件),如report中任何一个需要从外部传入的变量等(如一个Image对象所包括的char或报表title的字符串)。
和使用Fields一样,parameters也需要在创建的时候定义它的数据类型。parameters的数据类型是标准的java的Object。
在ireport中,Parameters的机制是允许用户通过应用程序传递参数致报表当中。在jasperreort中的某个对象中的expression可以通过下面的语法来访问一个当前ireport中存在的parameter:
$P{parameter name}。
如果应用程序没有为报表中定义的parameter赋值,那么parameter将会取我们在定义它的时候设置的default value的值。parameters是一个Java Object,所以如果它的类型是Object类型,我们在其default value里写下面的表达式就是错误的:
0.123
你必须要创建一个Object,如:
New Double(0.123)
这种写法就是正确的。
Parameters可以用来做SQL查询的条件参数的传递。如果你想根据部门编号(dept_id)得到客户的详细信息 (设计的时候我们是不知道这个部门编号的具体值是多少的).此时我们可以这样组织查询语句:
select * from employee where dept_id=$P{deptId}
此时SQL查询引擎将会采用PreparedStatement来处理传入的deptId值以此作为查询条件参数。
如果你想直接通过parameter 值来作为SQL语句的一部分,那么你可以使用下面的特殊语法:$P!{parameter name}。这种写法允许你在查询时用parameter的值来替换parameter name。例如,如果我们有一个参数名为MyWhere其值为:where dept_id=D11 ,查询写法如下:
Select * from employee $P!{MyWhere}
查询时实际提交的字符串是:
Select * from employee where dept_id=D11
在应用程序里,如果想把某个值传递到我们的报表中parameters时,我们可以把相关的值放入一个扩展java.util.Map接口类里,然后传入到ireport里。如下面的代码:
... Map hm = new HashMap(); ... JasperPrint print = JasperFillManager.fillReport(fileName,hm,new JREmptyDataSource()); |
fillReport是一个关键的方法,它允许你通过一个文件名,一个参数集来传递到我们要调用的报表当中。例-如通过下面的处理方法我们可以让一个处部的传入值作为我们报表的title。
下面的是一个例子:
a).声明一个parameter.这个parameter是java.lang.String 类型,名字为:name_report:
b).将这个parameter拖到page中的titleband.修改上面的程序代码如下:
HashMap hm = new HashMap(); hm.put(“name_report”,”report title”); ... JasperPrint print = JasperFillManager.fillReport(fileName,hm, new JREmptyDataSource()); |
运行时效果如下:
一般情况下,我们不需要为报表中的每一个parameters传递一个值,除非某一个parameter一定要从外部程序传入时我们才需为一个parameter设置一个值。如果一个parameter外部门程序没有为其赋值那么ireport将使用Default Value Expression来对一个parameter进行初始化,如果连Default Value Expression没有设置那么它的值就是null。
事实上,对于一个parameters我们不仅仅可以传递一个String,Integer,Boolean等java中小的对象,同时我们还可以利用parameters来传递一些足够大足够复杂的对象,比如一个图像(java.awt.Image),或者通过一个parameters来为一个为子报表提供的datasource 连接等。在用Map类型传递参数时我们要注意传递的参数要与report里的parameters类型保持一致,否则会抛出ClassCastException。
ireport内嵌的Parameters:
ireport提供了一些内建的parameters,这些parameters对于用户来说是只读的。内嵌的parameters列表如下:
参数名 |
说明 |
REPORT_PARAMETERS_MAP |
它是一个java.util.Map类型的对象,可以通过它来传给填充报表去填充报表里所有的parameters,在这个参数里还包含用户定义的parameters. |
REPORT_CONNECTION |
一个JDBC的Connection参数,报表可以根据传入的JDBC Connection来获到相应的数据 |
REPORT_DATASOURCE |
为报表中的Connection提供一个DataSource。外部传入的时候类型也要是DataSource类型 |
REPORT_SCRIPTLET |
报表生成时使用的Scriptlet实例,如果没有指定ScriptLet,那么这个parameters将使用net.sf.jasperreports.engine. |
IS_IGNORE_PAGINATION |
通过这个参数你可以控制分页功能是打开还是关闭。默认情况下,分页是打开的。但是如果导出为HTML或EXCEL时不进行分页处理。 |
REPORT_LOCALE |
它被用来设置报表所处的地区。如果没有提供地区,那么将采用系统默认的地区。 |
REPORT_TIME_ZONE |
设置报表的时区,如果没提供的话采用系统默认的。 |
REPORT_RESOURCE_BOUNDLE |
为报表指定要加载的资源文件,在国际化一章中介绍了如何使用资源文件来构建报表。 |
Variables是用来存储对某些字段计算后的结果,比如sum等。
和fields,parameters一样,Variables也是有数据类型的。你必须为一个Variable实例声明一个java类型。上图显示了如何创建一个新的Variable。看一下每个字段的意思:
Variable name: variable的名称,我们可以在表达式中通过下面的方法来引用一个Variable:
$V{variable name}
Variable Class Type : variable也可以是任意一个Java类型,在下拉框里你可以看到大多数常用的类型,如java.lang.String等。
Calculation Type : 一个计算类型可以用来计算结果。当一个预先定义的值是“nothing”时,那它的意思是不做任何计算。ireport会根据指定的计算类型和从数据源中获取的每一条记录计算出结果,然后去更改variables的值。做一个变量的计算意为着去执行一个表达式,如果计算类型为nothing,那么ireport将会根据variable expression为variable赋值。计算类型列举如下:
类型 |
说明 |
Nothing |
不做任何计算 |
Count |
对记录集数量进行循环累加(计算有多少条记录),这和sum不一样,sum的计算是针对数字的,会对数字进行累加操作。 |
Distinct Count |
也是对计算有多少条记录的累加操作,只是在累加过程中会忽略已经出现的记录(比如某个字段的值已经出现后就不会再记录) |
Sum |
添加每一次表达式(这里的表达式只能是数字类型)迭代的结果到变量中 |
Average |
它用算术的方法来计算表达式累加结果的平均值 |
Lowest |
返回表达式最小值 |
Highest |
返回表达式最大值 |
StandardDeviation |
根据表达式所有值返回标准方差 |
Variance |
返回表达式接收到的值的变化 |
System |
不做任何计算并且表达式也是没有价值的,在这样的用例中,报表引擎仅仅保存内存中variable的最后的值。 |
Reset Type : 重置类型。表示一个变量在什么时候做重置操作。reset类型如下:
类型名称 |
说明 |
None |
任何时候都不做重置操作 |
Report |
在报表创建的时候Variable才会对表达式进行初始化 |
Page |
Variable的初始化在每一页都做一次reset操作 |
Column |
Variable的初始化在每一列都做一次reset操作(这里的Column指的是report里的page的column,每个报表的column可以通过报表的Editàreport properties窗口进行修改) |
Group |
Variable的初始化在每一个组都做一次reset操作 |
Reset Group :如果group reset type被勾选,这里就要选择group名称(关于group可以参考后面的章节)。
Increment : 它指定当一个variable值被评估或当你想要这个组采用类似于subtotals 或averages来计算,increment 类型可以参考下面的表。
Increment Group : 如果这个group的increment type 被勾选它将指定这个组决定variable的increment。
Custom Incrementer Factory Class : 指定一个实现了JRIncrementerFactory接口的java类名称,通常用来定义一些诸如sum之类的非数字类型的操作。
Variable Expression : 它是一个java的expression,用来标识variable每一次迭代的值。
Initial Value Expression : 它是一个为variable初始化时采用的expression.
内置的variables:
和parameters一样,ireport提供了一些内置的variables(这些由报表引擎直接管理),这些变量是只读的,不能被用户修改,如下表所示:
Variable 名称 |
说明 |
PAGE_NUMBER |
它包含当前页的页。 |
COLUMN_NUMBER |
它包含当前的列数。 |
REPORT_COUNT |
当前的记录数目 |
PAGE_COUNT |
当前的页号 |
COLUMN_COUNT |
当前的列号 |
|
在 |
在本章中,我们将解释如何用iReports去管理bands和groups,在第四章中我们介绍了报表的结构,在本章中我们将要看到如何使用bands来划分报表。同时我们还将看到如何使用groups,如何创建report中的breaks,如何管理subtotals等等。
ireport把一个报表划分成七个主要的bands和一个名为backgroup的band(一共是八个bands),每当我们添加一个group时就会有两个新的band添加到报表当中:group footer bands和group header bands。
点击 按钮在弹出的窗口中我们可以对当前报表所具有的bands的进行相关属性的修改。
通过上图中的窗口,我们可以修改左边列表窗口中的任何一个band的属性,比如它的高(以像素为单位)。Print When expression表示我们可以在这里输入一个表达式,这样我们可以根据表达式的值来控制当前的这个band是否输出。这里需要注意的是Print When expression里必须返回一个java.lang.Boolean类型的对象,如:
New Boolean(true)
上面的表达式说明这个bands总会被打印(如果我们什么都不输入这个就是默认值),在表达式里可以使用fields,variables,parameters。根据这个特性我们可以通过相关的fields,variables,parameters来灵活的控制当前的band是否可以输出,比如下面的代码:
($F{SALARY}.intValue()>4000?new Boolean(true):newBoolean(false))
上面的代码说明当salary的值如果大于4000就输出当前的band否则就不输出当前的band。
把鼠标移到bands的边上我们可以鼠标拖拉的方式改变band的大小以此来适配band里的内容。
Groups允许我们对报表里的数据进行全新的切割分组组合。分组是通过一个表达式来定义的,ireport会通过这个表达式进行计算分组情况,每一个新的组都是从我们在expressionvalue里输入的表达式发生变化的时候开始的。接下来我们将通过一个例子的演示一步一步说明如何使用groups。
假设我们有一个员工的列表,我们想要创建一个根据员工所在的部门进行分组的报表。
a)首先我们打开一个新的报表文件,然后点击Connection/Data Sources,选择JDBC Connection,我们创建一个JDBC的Connection,如下图:
b)点击和工具栏里的按钮,输入查询语句,如下图
c)把Employee_no和Employee_department两个field拖到report 中的detail的band里,同时调整detail band的高度,如下图:
d)保存报表文件,并按浏览报表生成后的效果,如下图:
到这里,我们看到员工的数据已经可以在报表里显示,接下来我们就对员工信息按dept_id字段进行分组显示。
e)点击主工具栏里的按钮,打开groups定义窗口,并添加一个新的名为dept的group,如下图:
一个group有下面几个属性需要我们去定义:
Group name:为这个group指定一个名称,group创建完成后会产生两个新的band : header和footer band,这两个band的名称采用group的名称来定义。
Start on a new column:如果此选项勾选,那么分组只会在报表中的新的column产生,也就是说在一页中报表分组在一个column里只会做一次。
Start on a new page:与上面的选项类似,如果该选项勾选,那么分组只会在每一页做一次,也就是说每一页最多只会有一个分组数据。
Reset page number:该选项允许在一个新组开始的时候重设页号。
Print header on each page:如果该选项勾选,那么如果当前页没能显示完组里的所有数据时,到一页显示该数据时会把该组的header打印出来,否则将不会打印。
Min height to start new page:该选项用来设置当前报表中页面所余下的高度的最小值分组的最小值,默认值是0,如果我们设置为100,表示如果当前页面余下的高度小于100就不再继续显示分组的数据,而是在下一页显示分组的数据。
Group expression : 分组时采用的表达式规则,如上图中我们在定义dept这个group时我们在GroupExpression里输入了$F{DEPT_ID},表示将该组按员工的DEPT_ID字段进行分组显示。
Group header band height:新产生的header band的高度。
Group footer band height:新产生的footer band的高度。
f)接下来我们把在detail band里的dept_id的field移到report里新产生的deptHeader的band里,
g)点击按钮,报表的生成效果如下:
在jasperreport里group的数量是没有限制的,一个group可以有一个父group同时也可以有若干个子group。通过group定义列表,我们可以通过”move up”和”move down”来设置group的顺序。
Group 向导
在iReport中还有一种简单的方法创建一个group,那就是向导,通过向导我们可以快速的创建一个group,我们可以通过主菜单中的Edit new report groupwizard来启动创建group的向导。
向导允许用户创建最多有四个group的报表(group header,group footer和每一个group关联)。
Groups和records的排序
我们可以在设置查询的地方选择设置选项来使ireport自动对依据字段进行排序,我们也可以在创建一个group时要对分组的字段做好正确的排序工作。比如在sql中我们可以添加“order by ”来对要分组的字段进行排序。
SubReport是ireport提供的高级功能,通过SubReport,我们可以创建复杂的报表。通过SubReport,我们可以创建一个包含若干个子报表的报表。创建一个包含子报表的报表我们需要三个对象:一个jasper文件,一个包含参数map(当然可以为空)和一个DataSource(数据源,或者是一个JDBC的Connection)。在本章中我们将解释如何通过这三个对象创建一个SubReport并实现在子报表中数据过滤显示的目的。
一个SubReport是一个真正的包含它自己xml文件并且是一个已经编译好的jasper文件。创建一个SubReport就是创建一个普通的报表文件,创建过程中唯一需要注意的是设置SubReport的宽度、高度及一些不需要显示的band,一般情况下对于不显示的band我们把它们的高度都设置为0。在设置子报表的宽度时我们应该考虑到它在父报表中显示时的效果,然后针对这一特性有针对性的去设置。
在父报表中我们要添加一个子报表可以通过iReport中的SubReport工具 来实现。子报表的形状和一个距形类似,我们可以调整SubReport的尺寸和位置以控制子报表的显示。
将一个子报表链接到父报表中需要做三件事情:获得SubReport所对应的jasper报表对象、如何为它提供数据及如何为子报表的parameters设置具体的值。所有这些信息的定义我们可以通过SubReportproperties窗口来实现。
当我们在应用程序里通过fillReport方法来生成一个报表的时候,我们会提供一个包含参数值的Map一起传递到报表中,包含参数的Map对象是由报表引擎直接管理的。在子报表属性窗口的里的“SubReport”标签里的“Parameters Map Expression”属性,就是提供给我们的最简单的方法来为子报表设置parameters的值,它允许我们定义一个最终可以返回java.util.Map对象的表达式,使用这种方法我们可以把从外部应用程序传到父报表里的parameter对象,当然这个parameter的实际值是一个java.util.Map对象传递给子报表使用(比如$P{TestMap}),这里我们也可以使用内建的报表parameters:$P{REPORT_PARAMETES_MAP}把子报表中的java.util.Map对象传递到父报表中供使用。如果该属性我们空着不填那么一个不包含任何值的空的java.util.Map将会被传到子报表中。这种机制的局限性是parameters里所对应的java.util.Map值是死的、不会变的。为了克服这种局限性,jasperreport允许我们定义parameter键值对的时候每个对象的值通过一个表达式来创建,如在图中的“SubReportParameter”表中通过添加个参数java.util.Map来填充子报表。
deptId是子报表中一个parameter的名称,如果设置值时也要与子报表中deptId的数据类型保持一致。
指定数据源
为子报表指定数据源就是告诉jasperreport引擎如何获到数据来填充SubReport。我们可以指定两种类型的数据源:JDBC Connection和DataSource。
使用JDBC来填充报表是很简单的,在Connection Expression里定义一个已经连接到数据库的java.sql.Connection对象。要把一个已经处于打开状态下的连接传到SubReport中我们只需使用一个预定义的包含一个基于从应用程序里调用fillReport方法时的传入的连接REPORT_CONNECTION参数
如果使用一个DataSource就稍微复杂一点,事实上它是一个简单的记录的集合,记录集合的概念和JDBCConnection不同,既然如此我们可以通过一个parameter来传递一个DataSource来填充SubReport,此时采用这种机制内建的parameter REPORT_CONNECTION就不再起作用了。
一个DataSource是一个普通的“消费品”对象仅仅可用来填充一次报表。所以一个作parameter传递的DataSource可以满足一个SubReport的需要。因此parameter的方式不能满足当父报表的每一行记录都有一些子报表的数据与之对应(除非主报表中只有一条记录)。当我们解释DataSource时我们可以看到这个问题可以通过自定义DataSource方式解决。
指定SubReport
创建一个SubReport我们需要指定一个扩展名为.jasper文件,我们需要在SubReport属性窗口中设置SubReport Expression属性。表达式返回的类型必须与我们在下拉框里选择的类似保持一致,类型列表如下:
类型名称 |
含义 |
net.sf.jasperreports.engine.JasperReport |
在一个JasperReport对象里预加载一个jasper文件 |
Java.io.InputStream |
一个jasper文件的流 |
Java.net URL |
一个用来定义jasper文件的URL |
Java.io.File |
一个jasper文件的File对象 |
Java.lang.String |
Jasper文件的文件名 |
如果表达式是一个String类型,那么引擎将通过JRLoad来加载指定位置的jasper文件。
在这个例子中我们将采用示例数据库来创建一个简单的包含子报表的报表。我们要从两张表里取数据:dept(部门表)、employee(员工表),要达到的效果是根据部门表的部门ID取出该部门下的所有员工。类似的效果的报表我们在groups一章中使用group也实现了一次,这里我们将用SubReport来实现相同的效果。
新建一个空报表,新建一个名为deptId的parameter对象,如下图:
在这里我们在Default Value Expression里赋予了一个空的字符串,为什么要这样做呢,因为接下来我们要使用该parameter来作为SQL的查询条件的输入值,所以我们要对这个参数赋予一个初始化的值。
点击 打开SQL查询引擎窗口,输入下面的查询语句:
Select * from tb_employee where dept_id=$P{deptId}
在这里,我们在查询员工信息的时候加了一个部门的条件,如下图:
此时如果我们把相关的field拖到detail band里,然后点击 我们发现没有任何记录,原因是因为我们并没有为deptId赋值。
保存报表文件,我们命名为emp.jrxml,我们把这个报表文件作为下面将要引用的子报表文件。因为它将作为子报表被引用,所以我们需要调整该报表的不用显示的band的高度及detail band的高度,如下图:
在detail band里我们放置了三个字段:employee_name(员工姓名)、sex(性别)、birthday(出生日期)。
接下来我们需要建立一个父报表,关闭emp.jrxml文件,新建一个空的报表文件,命名为dept.jrxml。点击主工具栏里打开SQL查询窗口,输入下面的语句:
Select * from tb_position
如下图:
把dept_name这个field拖动到报表的detail band里,同时在detail band里添加一个了报表,在弹出的子报表向导中我们选择“Cancel”按钮,这里我们不采用向导来创建SubReport。如下图
双击子报表图标打开子报表属性设置窗口,在弹出的窗口中我们选择“SubReport”标签,在“Connection/DataSource Expression”属性里我们选择“UseConnection Expression”并且指定内建的parameter:REPORT_CONNECTION作为表达式用来存储JDBC连接,如下图:
切换到“Subreport(other)”标签窗口,指定如何找到子报表文件“emp.jasper”以及如何创建和修改在子报表与父报表之间的参数传递。
使用绝对路径在iReport里我们不用去考虑子报表文件是否可以被找到的问题,因为子报表存放的目录已经被自动的加到iReport的classpath里了。这里我们指定的值是“$P{SUBREPORT_DIR} +java.io.File.separator+"emp.jasper"”
为了在子报表里可以显示员工的信息我们需要为子报表的deptId这个parameter赋值,这里我们通过父报表传过去,在“Subreport parameters”窗口中,我们添加了一个用于为子报表的deptId赋值的parameter,该值到于父报表中的$F{DEPT_ID}这个field。编译子报表和父报表。
点击主工具栏中的按钮,查看生成的后父报表效果。
一个DataSource是Jasperreport获取数据以生成报表的源。这里有两种类型的DataSource:一种是JDBC Connection,用来从关系型数据库里取数据;另外一种是扩展了JRDataSource接口的java object,这种类型的对象允许我们去管理数据的细节,比如一个xml文件或一个javabean的集合。
通过sql 查询从关系型数据库里取数据来填充生成报表是很简单的,iReport可以通过各个数据库厂商提供的JDBC Driver来获取连接生成要检索的field。
如果我们不通过JDBC直接访问数据库来获得生成报表的字段与数据的时候,我们需要使用JRDataSource(全称JasperReport Data Source)。JRDataSource是一个接口,它允许我们访问具有行列结构的数据(在Datasource行我们叫records,列我们叫record fields)。
不管是JDBC Connection还是JRDataSource,他们都不能通过JasperReport来创建,但是当应用程序调用生成报表的时候,我们可以通过JasperReports的fillReport方法传递一个打开的数据库连接(一个java.sql.Connection对象)或者是一个JRDataSource对象实例用于填充被调用的报表。对于传递一个java.sql.Connection对象的情形,JasperReports将在指定的报表里使用JDBC Connection来执行一个SQL查询,查询的结果将会被包含在一个JRResultSetDataSource对象里(JRResultSetDataSource也是一个JRDataSource对象的实例),这样JasperReports将使用一个JRDataSource对象来关联打印数据。
在本章中,我们将阐述JRDataSource的不同类型和他们如何在iReport中使用。此外我们还将看到如何对一个JRDataSource做扩展;通常通过扩展一个datasource可以克服JsperREports自身的一些局限性,比如在交叉报表中使用等。
iReport允许我们管理和配置不同类型的DataSources用来填充报表。这些DataSources被储存在iReport配置文件中当我们需要的时候即可以使用。
我们可以使用的DataSources类型如下:
- JDBCConnection
- XMLDataSource
- JavaBeanCollection DataSource
- CSVDataSource
- CustomDataSource
- JRDataSourceProvider
- HibernateDataSource
打开状态的JDBC Connection在报表生成的时候会被直接传递到JasperReport中。XML DataSource允许我们从XML文档里获取数据用来填充报表。一个CSV DataSource允许我们打开一个CSV文件来填充报表。JavaBean Collection DataSource,CustomDataSource和JRDataSourceProvider他们允许我们一个写好的java类来获取数据。Hibernate DataSource定义了一个执行HQL语言的方法。DataSource的管理我们可以通过在主菜单“Data Connections/DataSources”(图10.1)打开配置管理DataSource窗口界面。
从技术的角度去看一个Connection和一个DataSource是两个不同的对象(Connection需要一个关系一个关系型数据库,但是一个DataSource只是提供一个简单的接口用来访问数据结构)。
虽然我们可以建立了若干个DataSource准备使用,但是iReport在工作时同一时候总是使用一个DataSource或一个connection。因此你需要设置一个“active”datasource。设置一个dataSource为“active”最简单的方法是在工具条上的下拉框里选择一个你要使用的datasource,那么这个datasource就处于“active”状态了。
同时你也可以从主菜单里的“DataàSet the active connection”在弹出的窗口中选择一个DataSource并设置其为“active”状态。
图62
最后我们还可以在DataSource窗口里通过“set default”来设置“active”状态的DataSource。
如果没有datasource处理“active”处于激活状态,那么iReport里生成的报表里面将不会产生数据。同时当我们使用报表向导的时候要求必须要有一个处于“active”状态的JDBC Connection。
一个JDBC Connection允许我们从一个关系型数据库里直接获取数据(需要有一个JDBCDriver)。要添加一个新的JDBC Connection,可以点击“New”按钮打开创建新连接的管理窗口。
选择“Database JDBC Connection”点击“Next”,在出现的窗口中输入数据源的名称及Driver、URL、username,password等相关信息。
点击“test”按钮,对JDBC连接做测试。
完成新建JDBC Connection之后,别忘了在管理窗口中设置Connection的“active”状态。
当报表采用的是一个JDBC Connection创建的时候,用户需要指定一个SQL语句用来从数据库里取出数据。主报表的Connection也可以为其子报表提供服务。例如,JasperReport里的REPORT_CONNECTION,一个java.sqlConnection类型的内建参数对象放在表达式里提供给子报表使用。
$P{REPORT_CONNECTION}
这个参数包含的是一个正确的从应用程序里传入的java.sql.Connection对象。使用JDBC或一个SQL Connection是一种最简单的最容易的方式用来填充报表,关于创建SQL查询的细节我们将在11章中讲解。
为了可以在报表中使用通过SQL查询出来的fields,我们需要“Register”它们(要注册的可能不是全部是字段,仅仅只是我们在报表中要使用的字段)。对于每一个字段我们需要指定一个名称和字段的类型,下表中显示了SQL类型所对应的Java类型。
SQL 类型 |
JAVA类型 |
CHAR |
String |
VARCHAR |
String |
LONGVARCHAR |
String |
NUMERIC |
Java.math.BigDecimal |
DECIMAL |
Java.math.BigDecimal |
BIT |
Boolean |
INTYING |
Integer |
SMALLINT |
Integer |
INTEGER |
Integer |
BINARY |
Byte[] |
VARBINARY |
Byte[] |
LONGVARBINARY |
Byte[] |
DATE |
Java.sql.Date |
TIME |
Java.sql.Time |
TIMESTAMP |
Java.sql.Timestamp |
在上面的表当中对于BLOG和CLOB类型和其它的特殊类型如ARRAY、STRUCT,REF等我们没有指定对应的JAVA类型,这是因为这些类型在JasperReports里不能自动管理它们。
除了使用SQL做为数据源之外,我们还可以使用其它类型的数据源。在使用之前,我们需要明白如何使用JRDataSource接口。对于实现每一个JRDataSource接口的类必须实现两个方法:
Public Boolean next()
Public Object getFieldValue(JRField jrField)
对于第一个方法是用来移动一个虚拟的游标到下一条记录,判断是否还有记录。如果Next方法返回true表示还有记录,否则表示已经没有记录了。
当JasperReports执行next方法时,在报表里声明的所有字段将被赋上相应的值并且所有的表达式重新计算它的值,接下来将决定是否在一个新的group里打印header,是否开始一新的页等。当next方法返回false时,报表的内容也就宣告结束同时开始打印所有的最后的bands(group footer,column footer,last pagefooter 和summary)。很多时候需要调用next方法用来取下一条记录。
当JasperReports调用完成next方法后如果返回true的时候就调用getFieldValue方法。事实上报表里的每一个field在取值的时候都会调用getFieldValue方法(查看第七章看如何声明一个报表的field)。getFieldValue方法返回数据的类型必须与声明的字段类型相匹配,当然如果返回一个null除外。
在DataSource里面我们还可以使用一些JavaBean做为数据源来填充数据,这里的JavaBean指的是一个POJO,里面private类型的若干个属性和与之对应的getter方法:
Public
这里的
要创建这种类型的connection我们只需在创建的时候选择“JavaBeans set datasource”
点击“Next”输入“JavaBeans set data source”的名称,JavaBean set datasource使用一个扩展类(一个工厂类)来生成我们所需要的JavaBean对象,然后组合成数据传递到报表当中。输入包含包名的java class名称,它需要有一个static方法产生一个包含若干个javaBean的Collection类型或一个array(Object[])类型。让我们来看看如何去写这个工厂类。
假如你的数据是一个Person对象,它包含两个fields:name和age:
package com.asinfo.ccm.model;
import java.io.Serializable;
public class Person implements Serializable { private Stringname; private int age;
public Person(String name,int age){ this.age = age; this.name = name; }
public int getAge() { returnage; }
public void setAge(int age) { this.age = age; }
public String getName() { returnname; }
public void setName(String name) { this.name = name; }
}
|
接下来我们要写我们的工厂类,我们这里的工厂类名为TestFactory
package com.asinfo.ccm.model;
import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List;
public class TestFactory { private static Person[] data = { new Person("用户1" ,1), new Person("用户2" , 2), new Person("用户3" , 3), new Person("用户4" , 4), new Person("用户5" , 5), new Person("用户6" , 6), new Person("用户7" , 7), new Person("用户8" , 8), new Person("用户9" , 9), new Person("用户10" , 10) }; public static Collection generateCollection(){ return Arrays.asList(data); } }
|
我们的DataSource提供了一个包含10个Person类型的JavaBean。使用JavaBean作为DataSource的特点是,它的字段必须通过getter方法暴露在外面,意思是如果我们的JavaBean有一个getXyz方法,那么xyz就是一条记录的field,在我们的例子中我们的是name和age。创建一个新的创建文件,然后添加两个名为name和age的字段:
接下来把这两个字段拖到报表detail当中。
将前面我们做的两个类添加到iReport中的classpath当中。添加一个JavaBean set data source,名为TestBeanDataSource,并将其设置为“active”,如下图:
启动生成报表效果如下:
在JasperReports中提供了一个名为JREmptyDataSource特殊的DataSource,这个DataSource是当我们点击 按钮生成报表时调用。这个DataSource的next方法会根据记录的数量返回true,同时每次调用getFieldValue方法时总是返回null值。这种操作有些类似于一些记录没有字段一样,这就是一个空的DataSource。
该项类有两个构造方法:
Public JREmptyDataSource(int count)
Public JREmptyDataSource()
第一个构造方法让我们告诉这个DataSource该返回多少条记录,第二个构造方法会把记录的总数设置为1。
当我们点击按钮生成报表时,我们可以按住shift键来设置报表里产生的空记录数。如下图:
从jasperreports1.2版开始,Jasperreports提供了一种直接使用HQL(Hibernate Query Language)来创建报表。要使用这个功能,首先我们需要设置一个Hibernate connection。把所有的JAVA编译好的类,jar包和使用Hibernate mapping的配置文件放在classpath里。换句话说,iReport必须要保证可以访问所有的你需要使用的*.hbm.xml文件。
把这些对象添加到classpath中的方法是,选择主菜单中的ToolsàClasspath,在弹出的窗口中设置你需要加入到classpath中的对象。
接下来我们需要打开DataSource对话框,点击“New”新建一个DataSource,不过这时我们选择Hibernate connection作为我们的DataSource类别。点击“Next”输入connection的名称。再次点击“Next”进入下上步设置,如下图:
在点击“test”按钮时要保证hibernate.cfg.xml在你的classpath里。
同时设置这个“test hibernate”为active状态。此时iReport仅使用Hibernate Configuration工作。
从主菜单中的ViewàReport query里打开查询窗口,在Query language里我们选择Hibernate QueryLanguage(HQL),然后输入相应的HQL,接下来的工作就像使用SQL一样啦。
有时使用Jasperreports所内置的JRDataSource不能完全满足我们的要求。此时我们可能需要写一个自己的新的JRDataSource。自定义一个JRDataSource并不复杂,事实上创建一个自定义的JRDataSource就是实现JRDataSource接口类,重写里面的next和getFieldValue方法就OK啦。
Package net.sf.jasperreports.engine Public interface JRDataSource{ Public Boolean next() throws JRException; Public Object getFieldValue(JRField jrField) throws JRException; } |
这里的next方法是用来把记录移动到下一条,同时判断还有没有记录。如果返回true表示还有记录可用,否则表示没有可用记录了。
当next方法调用时,getFieldValue方法将会返回请求字段的值(如果请求的字段没找到或不存在,那么将返回null)。一般来说,请求字段名会作为参数包含在JRField对象里。JRField是一个接口对象,通过它我们可以得到三种信息:字段名称,描述,和字段类型。
接下来我们就来写一个我们自己的DataSource。我们的这个DataSource要实现对一个目录进行浏览并返回里面的文件或文件夹。DataSource的field有以下几方面:文件名(fileName),一个标志位(isDir)用来表示它是一个文件还是一个文件夹,文件大小(size)。
我们的DataSource有两个构造方法,一个构造方法有一个参数可以用来接收要扫描目录;一个就是没有参数的构造方法(那么将采用默认的目录进行扫描)。
我们这个DataSource将会根据指定的目录查询文件和文件夹,并且以一个文件数组的形提供出来。
test; java.io.File; net.sf.jasperreports.engine.JRDataSource; net.sf.jasperreports.engine.JRException; net.sf.jasperreports.engine.JRField; JRFileSystemDataSource JRDataSource{ File[] fileArray; index=-1; JRFileSystemDataSource(String path){ File f= File(path); (f.exists() && f.isDirectory()){ fileArray=f.listFiles(); } } JRFileSystemDataSource(){ ("."); } Object getFieldValue(JRField arg0) JRException { File f=fileArray[index]; (f==) ; (arg0.getName().equals("fileName")){ f.getName(); } (arg0.getName().equals("isDir")){ Boolean(f.isDirectory()); } (arg0.getName().equals("size")){ Long(f.length()); }{ ; } } next() JRException { flag=; index++; (fileArray!= && index flag=; }
flag; } } |
到这里我们自定义的一个JRDataSource已经完成了,接下来我们将会来解释如何在报表中使用我们自己的的DataSource。
为了使用这个DataSource,接下来我们需要编写一个JavaBean set DataSource,这个和我们前面写过的很像,代码如下:
test; net.sf.jasperreports.engine.JRDataSource; TestFileSystemDataSource { JRDataSource getFileSystemData(){ JRFileSystemDataSource("d:/doc"); } } |
这个类中的static方法将会被调用,执行并产生一个新的JRFileSystemDataSource对象,同时指定该对象里的文件夹路径为“d:/doc”目录。
接下来我们可以新建一个connection,创建时我们选择“Custom JRDataSource”类型,并设置它的名称为“Test FileSystemDataSource”,如下图:
接下来我们需要在页面中新建三个field,分别是fileName(String),isDir(Boolean),size(Long),并将他们拖到报表的detail band中,如下图:
运行报表,生成效果如下:
对于这种生成的格式,为了美观期间我们可以对其进行格式化。把“目录/文件”拿掉,通过在“文件名”列前面加图片的方式来显示该文件是文件还是文件夹。
在detail里,在fileName列前加一个image对象,并在其Expression里加上如下代码:
($F{isDir}.booleanValue()?"D:\\iReport-2.0.1\\report test\\folder.gif":"D:\\iReport-2.0.1\\report test\\file.gif") |
该句话的含义是:如果isDir返回的为true则输入文件夹图片,否则输入文件图片,如下图:
设置报表的列数为2,报表排版如下:
启动报表,生成的效果如下:
国际化意味着报表可以根据不同的地区显示不同的语言文字。报表所要显示的不同的语言文字是存储在资源文件里。在本章中我们将介绍怎样使用内置函数msg()来处理国际化问题。
当我们想让一个报表实现国际化功能时,我们需要定位所资源文件里的名称字符串,就是label或者叫key,key与value是一对字符串,中间用“=”连接。如:
Title_username=用户名 Title_phone=电话号码 |
保存这些key与value对的文件以“.properties”为后缀。在运行期间,报表引擎将会在classpath里查找以“.properties”结尾的资源文件。如果文件找到了,那么就从文件里读取相应的资源信息。资源文件的指定是通过报表属性窗口的“i18n”来实现的。
一般来说,一个资源文件名的组成包括三个部分:一个是资源文件名,一个是国家地区代码(与文件名之间以“_”连接),最后就是文件的扩展名“.properties”。对于默认的资源文件名来说是没有国家地区代码的。完整的资源文件的格式如下:
|
比如:
I18nReport_fr_CA_UNIX.properties I18nReport_zh_CN.properties I18nReport_en.properties I18nReport.properties |
前面格式里提到的“other code”(比如上面的“_UNIX”)一般不去指定它。
在iReport中,它有能力自己去管理资源文件,资源文件我们需要把它和报表文件(jrxml文件)放在同一目录,并且资源文件的名称也要与报表文件的名称保持一致。
我们可以从主菜单的“EditàInternationalication”的“Localization files”打开资源文件管理窗口。如下图:
我们可以通过“Create New Locale”按钮创建一个新的资源文件。
创建时我们需要指定文件名及国家代码,创建完成之后我可以通过“Modify file”按钮来对资源文件的内容做相应的修改。
得到资源文件里的key所对应的vlaue我们可以通过下面的方法实现:一种是使用JasperReports内部的表达式str(“key name”),或者是使用表达式$R{keyname}:
$R{test.name}
在JasperReport中的国际化我们可以使用函数msg,通过使用msg函数我们可以对取到的国际化的值做相应的格式化处理。比如我们的资源文件中一个key/value对为:
title=my name is {0}
此时我们需要为{0}指定具体的值,通过使用msg函数我们可以对这里的{0}进行填值处理:
msg($R{title},"gaojie")
这样在报表生成后最终的值就是:my name is gaojie
我们有三种方式来使用msg函数:
Public String msg(String pattern,Object arg0) Public String msg(String pattern,Object arg0, Object arg1) Public String msg(String pattern,Object arg0, Object arg1, Object arg2) |
在iReport中我们可以指定所在地区,这样我们就可以控制显示的语言。点击主菜单“Buildàset report local or BuildàSet time zone”,在弹出的窗口中我们可以对地区做相应的修改。
Scriptlet是一个java class用来在报表生成的时候执行特定的动作(比如新页开始的时候或者处理结束的时候)。在本章当中我们将要看如何写一个简单的Scriptlet以及如何在报表里使用它们。
要写一个scriptlet我们只需扩展java类net.sf.jaspererports.engine.JRAbstractScriptlet。在该类中可以触发报表生成的时候,数据产生的时候的事件,同时我们可以访问报表中的所有的variables,fields,parameters。
最简单的scriptlet实现类是jasperreports提供的JRDefaultScriptlet:
package net.sf.jasperreports.engine; public class JRDefaultScriptlet extends JRAbstractScriptlet { public JRDefaultScriptlet() { } public void beforeReportInit() throws JRScriptletException {} public void afterReportInit() throws JRScriptletException {} public void beforePageInit() throws JRScriptletException {} public void afterPageInit() throws JRScriptletException {} public void beforeColumnInit() throws JRScriptletException {} public void afterColumnInit() throws JRScriptletException {} public void beforeGroupInit(String groupName) throws JRScriptletException {} public void afterGroupInit(String groupName) throws JRScriptletException {} public void beforeDetailEval() throws JRScriptletException {} public void afterDetailEval() throws JRScriptletException {} |
就像我们看到的那样,该是一系列的after和before组成的方法,这些方法代表着不同类型事件的触发。
事件/方法 |
描述 |
Before Report Init |
当报表初始化的之前时候调用该方法 |
After Report Init |
当所有的的variables初始化之前的时候触发该方法 |
Before Page Init |
当产生一新页的时候,所有variables初始化之前调用 |
After Page Init |
当产生新页时,所有的variable初始化之后调用 |
Before Column Init |
当产生一个新的column时,在所有的variables的resetType为“columns”时初始化之前调用 |
After Column Init |
当产生一个新的column时,在所有的variables的resetType为“columns”时初始化之后调用 |
Before Group |
当group x调用的时候,并且所有的variables 的resetType为“group”,group x 初始化之前调用 |
After Group |
当group x调用的时候,并且所有的variables 的resetType为“group”,group x 初始化之后调用 |
Before Detail Eval |
在detail band打印的时候之前调用 |
After Detail Eval |
在detail band打印的时候之后调用 |
在Scriptlet内部我们可以使用下面的Map得到所有的fields,variables和parameters,这些Map是:fieldsMap,variablesMap和ParametersMap。
在iReport 中最有用的工具之一是使用向导利用templates来创建报表,在本章里我们将解释如何创建一个自定义的templates,如何把它们添加到我们的模版库里。
一个模版文件就是一个普通的jrxml文件。当使用向导创建一个新报表时,选择的模版文件将会被加载,同时会对用户输入地方做相应修改。
有两种类型的模版:columnar 和tabular。columnar是一种用一个静态的text显示field name,同时用一个textfields显示field的值的竖排的记录报表。
而Tabular类型将会像下图这样显示所有记录
模版是一些放在iReport安装目录下templates目录里的jrxml文件。iReport根据文件的名称来决定它是columnar类型还是tabular类型的模版。如果文件名以T结尾,那么它将作为一个tabular类型的模版。如果它是以C结尾的话它将会被做为columnar类型模版。下面是iReport提供的模版列表:
File |
Report type |
classiC.xml |
Columnar report |
classicT.xml |
Tabular report |
Classic_landscapeT.xml |
Tabular report |
graycC.xml |
Columnar report |
grayT.xml |
Tabular report |
Gray_landscapeT.xml |
Tabular report |
我们打开classicC.xml文件,查看之后我们可以明白一个模版的结构。在这个文件里你可以发现四个组:group1,group2,group3,group4,title band和group footer可见,columns band不可见(因为对于columnar 类型的报表columns band没有用)。在detail band里有一些为将来每一个field准备的静态的text label作为模版的label,textfield是一个真正的field。在particular里每一个group可以通过n包含一个图形元素和一些静态text元素:
GnLabel
这里的n代表group的编号,textfield元素包含下面指定的表达式:
GnField
这个元素将包含使用group表达式的值。
Detail部分至少要包含像下面一样的一个静态文本:
DetailLabel
同时一个textfield元素用下面的表达式:
DetailField
其它的bands可以包含任何元素。在这些bands中将会在使用模版时生成。
表格类型的报表模版的设计和上面很相似,图13.4是classicT.xml在窗口里显示时的情形。
同样,有四个group,在它们之前有一个column header,它需插入一个静态的字符串作为columns labels。
在detail band区域,仅仅只有一个DetailField元素将被作为所有列的一个列名标签。
模版是不能编译的,这是因为在textfield里包含的表达式不是一个合法的java表达式。
下面我们就来看一下怎么创建和使用一个模版。最简单的方法是打开一个已经存在的模版,选择一个最接近我们想要的模版的类型,然后按我们想要的对其进行修改,对存在的元素进行修改、添加新的元素或者删除不需要的元素。
为了使用模版,我们必须把改好的模版导入到templates目录里。记住使用.xml作为扩展名,例子中我们命名为mytemplateT.xml。添加C或T字母到模版的扩展名之前表示模版的类别。
以上工作完成后,我们可以通过向导来创建一个新的报表。在模版选择处我们可以看到新的我们刚才添加的报表模版在模版列表当中。
这里,你应该可以看到我们自定义的模版没有预览图可用,要想让我们自定义的模版关联到一个预览图可以在templates文件夹里放上一个150x150的gif图片,并重启iReport。(在这个例子当中我们用的图片名叫: mytemplateT.gif).
图中显示了在向导窗口中当我们选择我们自定义的模版时出现的预览图。
使用模版可以帮助我们提高开发报表的效率。通过使用我们自己的模版,我们创建的报表生成的效果如下:
我们知道模版文件都是放置在模版目录里面的。这样的话当iReport访问远程文件系统时可能会带来一个问题,当发展到0.5版本的时候我们可以把模版文件放到jar包里去,每一个jar可以包含一个或多个模版和一个在ireport目录下的名为template.xml的模版描述文件。当报表向导执行时,iReport将要查找所有的在classpath下的可用的名为/ireport/templates.xml文件。
为了了解如何使用一个jar提供一个模版,我们来创建一个基于名为Classic TemplateResource模版的例子。
Template.xml的内容如下:
|
Template.xml定义了一个模版的集合,每一个模版我们都可以定义它的名字,类型(columnar或者Tabular),和图标。
这个jrxml和图标用“/”开始指定,我们jar里的内部目录树可能类似于下面这种。
template_sample.jar
├───ireport
├───it
│ └───businesslogic
│ └───ireport
│ └───templates
└───META-INF
Template.xml将要被放到ireport目录下,同时所有的其它文件(如这个例子中的 classic.xmlt和classic.gif)将会被放到/it/businesslogic/ireport/templates目录下。
把创建好的jar放到classpath里,这时即使在模版目录里没有它我们也可以使用啦。
iReport很多的图表(Chart)类型可提供使用。iReport现在支持Pie,Pie 3D,Bar,Bar 3D,XY Bar,Stacked Bar,Stacked Bar 3D,Line,XYLine,Area,XY Area,Scatter Plot,Bubble,Time Series,High Low Open Close和Candlestick 多种类型图表。
iReport使用jfreechart作为其图表引擎,jfreechart是一个强大的chart 生成工具。
创建chart的机制是建立在dataset的概念之上的,这就使得数据的管理和对更多复杂图表类型如High Low 之类实现可视化。
在这一章节里我们将来学习如何使用chart 工具一步一步在一个报表里创建一个Pie3D的chart。在这个例子中我们将使用doradosample里的数据库作为数据源。
建立一个空白的文档,点击打开查询窗口并且写下如下查询语句:
select employee_department,count(*) count fromtb_employee group by employee_department
报表的查询窗口
这里我们想生成一个chart用来显示根据学历员工数目。点击OK确认,iReport将注册查询得到的fields。同时把这些fields从objects library里拖出放入detail的band里。
选择chart tool 在summary里放置一个新的chart。
从charts窗口里我们选择pie3D图标并按OK按钮。
要配置chart,双击chart打开属性窗口,同时你可以通过右键菜单的方式打开chart的属性窗口。
点击“Edit chart properties”按钮出现的chart属性窗口。
这个窗口包含两个标签:“Chart properties”和“Chart Data”。第一个标签包含控件的parameters列表;第二个标签是用来决定和当前图片关联的数据。修改图形的不同样式,设置背景的alpha和前景的alpha为0.5和depth factor(深度因子)为0.2。
下一步定义和图形关联的data。切换到“Chart Data”标签。
在“Type of dataset”列表框中允许你指定一个dataset用来生成图形。在“dataset”标签里你可以在报表的上下文里指定一个dataset。Reset type和Reset group 允许你对dataset进行重置。
为了我们的需要,我们设置Reset type 为“Report”。当我们不想数据被重置时。同时我们设置Increment type 为None,这样每一条记录都将被附加到我们的dataset里。
在Detail标签里允许你键入一个表达式和每一个在datasource里单独的值关联。针对Pie 图表类型,有三个表达式需要我们输入:Key,Value和Label。
表达式Key允许你标识Pie图里的一块。如果Key的值出现重复,那么Label和Value的值会关联起来用来覆盖Key的值。一个Key值不能为null。Value的表达式值指定这个Key的数字值。
Label表达式的值允许你为并图中每一块指定一个标签。这个表达式的值是可选的,同时默认值为“
确认对chart的修改,保存文件并且点击 按钮启动报表。在图14.9里我们可以看到最后的效果。
当报表生成的时候,chart会去收集数据然后存储在相关的dataset里。Dataset类型有:
Pie
Category
Time period
Time series
XY
XYZ
High-Low
可以把dataset想成是一个表格,每一个dataset都有不同的列(fields)。当一条新记录插入到dataset里时值将会被添加到fields里。
我们可以通过图显示的窗口来管理chart的外观。在这个窗口里你可以查看并编辑charts和graphs的通用属性(如title,图例等)。
你可以使用表达式编辑框来更改Chart title 和Chart subtitle表达式
JasperReports只是利用了JFreeChart图表库的一小部分功能。如果你想自定义一个图表你可以写一个实现下面的接口的类:
Net.sf.jasperreports.engine.JRChartCustomizer
从这个接口中我们只需要实现一个方法:
Public void customize(JFreeChart chart,JRChartjasperChart);
在这个方法里用JFreeChart和JRChart作为它的参数。JFreeChart作为第一个参数通常是用来产生图片的,第二个参数包含用户在设计阶段一些参数。
报表的生成是基于一个data source(一个查询,一个java 的collection或者一个xml)。当你想要处理一个chart或一个crosstab时,这时可能上面的data source就不行了。一个subdataset 可用来在报表里提供另外一种记录集。你可能会使用一个subdataset用来填充一个chart或一个crosstab。在一个报表里你可以有任意多个subdatasets。
一个subdataset可以有它自己的fields,variables,parameters,同时如果需要它还可以执行一个查询。Dataset里的记录可以像主报表一样进行一次或多次分组。在组里可以使用subdataset的variables。一个subdataset通过一个叫“dataset run”的选项和一个chart或一个crosstab关联。
要创建一个新的subdataset,可以在工程library里右键选择“add-sub dataset”方式完成。
Subdataset的创建窗口打开。
这里你需要设置subdataset的名称,同时你也可以使用该subdataset合适的“when resource missing type”指定一个resourcebundle(查看关于资源文件的国际化章节)。
Jasperreports允许使用scriptlet在一个subdataset的records里执行一个特殊的计算。如果你需要你可以指定一个你的scriptlet类的名称。点击“create”按钮把这个subdataset添加到报表当中。
你可以在library标签里或者在subdataset列表对话框里查看刚才新建的subdataset (主菜单中Edit->sub dataset)
你可以使用和在主报表中相同的方法添加fields,variables和parametres到你的subdataset里。 Subdataset窗口里的“Query”按钮可以打开查询工具用来指定一个查询(像在主报表里一样),你可以用一个查询或一个其它的数据源(如javabean等)来反向得到字段。
你可以在一个chart里或一个crosstab里使用subdataset。JasperReports 需要一些扩展信息诸如jdbc connection执行sql 查询或者为一个指定的subdataset的parameter用来填充dataset。所有这些信息的提供者是一个dataset run。
Dataset 的工作方式与在主报表中引用一个SubReport类似。你可以使用表达式(里面可以包含主报表里的对象,如:fields,variables,parameters)为指定的subdataset的parameters设置值。在启动运行时可以定义一个parameters Map来为subdataset parameters填充值,同时还可以定义一个连接或一个datasource在subdataset获取数据时使用。
实例
在这个例子中我们将演示如何使用一个subdataset去填充一个chart。
在主报表中只有一条统计所有记录总数的记录(Select count(*) fromhelp_category)
第一步,创建一个新的空白报表
第二步,打开查询工具( )同时插入下面的sql语句:
|
第三步,从library 标签里拖动字段employeeCount到detail band如下图添加一个labels
第四步,创建subdataset
在library里右键选择弹出菜单选择Add->sub Dataset,我们创建一个名为subdataset1的subdataset。
第五步,在subdataset的对话框里点击“Query”按钮,我们键入如下语句:
select parent_category_id, count(*) as empCount fromhelp_category group by parent_category_id
第六步,添加pie3d chart到报表里
第七步,编辑chart属性
打开chart属性编辑窗口我们来编辑一下chart的相关数据,选择subdataset1作为它的subdataset。切换到“Connection/Datasource exp”标签同时从下拉框里选择“useconnection expression”。这里我们使用主报表里的connection来填充subdataset($P{REPORT_CONNECTION}) 。
第八步,设置Pie Dataset的属性
切换到“Detail”标签,我们输入下面的表达式:
Key expression: $F{DEGREE}
Value expression : $F{EMPCOUNT}
Label expression : $F{DEGREE}
第九步,保存并启动报表
第十步,报表制作完成
一个CrossTabs是一种在设计的时候既不确定行数和也不确定列数的一种表格,在运行环境下它会像下面显示的不同年份的一个销售报表一样。
水果/年份 |
2004 |
2005 |
2006 |
草莓 |
|
|
|
野生樱桃 |
|
|
|
CrossTabs在jasperresports中从1.1.0版本时开始提供,同时iReport也从1.1.0开始支持CrossTabs。
Jasperreports的CrossTabs工具允许对行和列的数据进行分组、汇总和自定义每一个cell里的内容。填充CrossTab的数据可以来自主报表里的dataset或来自subDataset。使用iReport里提供的向导我们可以简单快速的创建功能强大的交叉报表组件。
一个CrossTabs本质上是一个表格,行和列的数量取决于填充这个表格的数据。行和列也可以在groups里做聚合操作。对于每一个行或列的group我们都可以得到一个细节信息和一个可选的行列数据的汇总。
为了说明怎么让一个crosstab工作起来,我们将使用向导创建一个crosstab,当我们在工具条里选择crosstab元素并将其添加到报表中时crosstab的向导会自动启动。
还是以DoradoSample里提供的hsql数据里的employee报为例,我们使用包含下面查询语句的空报表开始:
Select * from employee
我们把crosstab放在报表的底部:summary band
在第一步里我们需要选择一个dataset来填充crosstab,我们这里使用主报表里提供的dataset,点击下一步继续。
在第二步里我们需要定义至少一个行分组.我们这里选择对所有记录使用DEPT_ID进行分组。这样就意味着crosstab的每一行将会采用一个明确的部门编号,这样JasperReports将会使用部门编号对数据集里的数据进行重新整理计算。使用向导,我们可以定义两个行分组,这是使用向导的局限性所在,事实上,如果你需要的话你可以通过Crosstab的属性设置窗口定义若干个行和列的分组。点击下一步继续。
和定义行分组一样,从这里我们可以定义两个列分组,这里我们仅使用一个列分组,使用数据中的DEGREE字段对数据进行分组。该字段的含义是学历,这就表示,我们要做的这个交叉报表是用来计算不同部门员工在学历方面的人数分布情况。
是时候定义detail 数据了,一般的,这个detail是使用类似根据country,year得到orders总数或者是相同组合的物品总数的一个聚合函数的计算出来的结果值。我们这里选择打印员工的数量(Detail field:ordered,function:count)。点击下一步继续。
最后一步我们可以为crosstab的布局设定一个布局。我们可以设置是否能看到表格线、或者是否包括行和列总数统计之类。我们这里全部选择。
点击finish
注意当一个crosstab添加到一个报表中后,会自动在设计窗口中添加一个用来编辑当前crosstab的tab页窗口。
当点击crosstab的tab页,两个新的属性窗口将会被添加在窗口的左边和右边:一个crosstab的结构树,用来显示crosstab的当前选中单元格和帮助相关信息。一个是crossstab对象列表用来显示和该CrossTab相关的一些variables或fields等。
当点击工具条上的启动按钮后,我们将可以看到引擎生成的如下效果的报表:
最后一列包含每一行交叉所有列的总计,最后一行包含每一列交叉所有行的总计。
一个crosstab至少必须有一个行分组和一个列分组,每一个行和列的分组都有一个可选的行/列的统计。
下面图中所示的是一个基本的由一个行列分组构成的crosstab。
Crosstab header cell |
Column group1 header |
Column group 1 total header |
Row group 1 header |
detail |
Col group 1 total |
Row group 1 total header |
Row group1 total |
Row group 1 total/column group 1 total |
当再次添加一个行分组时,这个crosstab 将会显示出如下效果:
Crosstab header cell |
Column group1 header |
Column group 1 total header |
|
Row group 1 header |
Row group 2 header |
detail |
Col group 1 total |
Row group 2 total header |
Row group 2 total |
Row group 2 total/column group 1 total |
|
Row group 1 total header |
Row group1 total |
Row group 1 total/column group 1 total |
一个行或列的分组可能通过crosstab的属性窗口来对它进行修改(在crosstab编辑器右键菜单里选择Crosstab properties)。
点击“row and column groups”的标签页。在这里我们可以看到已经定义好的行或列的分组。
添加一个新的分组请点击名为Add的按钮。
所有分组必须有一个唯一的名称。当你添加或修改一个行分组时,你可以设置rowgroup header的宽度。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Column group1 header |
Column group 1 total header |
Row group 1 header |
detail |
Col group 1 total |
Row group 1 total header |
Row group1 total |
Row group 1 total/column group 1 total |
“bucket”是分组的标准,它通过一个java类来定义。在一个bucket表达式里,你可以使用主报表里所有的fields/variables/parameters。但是,如果你选择使用subdataset来填充crosstab,那么必须使用subdataset里提供的fields/variables/parameters。
一个header的单元格增长依赖于它所跨越的行数。你可以选择多个元素放置在header里。
|
|
|
|
最后,你可以选择是否打印出总计的结果。这些选项是:
None : 没有总计行被打印
Start : 总计行打印在detail行之前
End : 总计行打印在detail行之后
创建column的group 是非常简单的。定义column group 时你可以定义column的高度来替代行高(看下图)
Column group height |
|
Column group1 header |
Column group 1 total header |
Row group 1 header |
detail |
Col group 1 total |
Row group 1 total header |
Row group1 total |
Row group 1 total/column group 1 total |
当Bucket在行分组里时,header的单元格内容的定位相似。但是在这个用例中, details可以改变header宽度而不是高度。
|
|
|
|
|
|
|
再一次,如果打印总计列的话,你可以做一些选择。各选项如下:
None : 没有总计列被打印
Start : 总计列在detail列之前打印
End : 总计列在detail列之后打印
列和行的高度可以很容易的在crosstab编辑器里通过鼠标拖曳表格线的方式改变。
当一个行或列添加到crosstab里时,一个特殊的用来引用bucket表达式的变量将要被创建。它和新的group有相同的名称。你可以从crosstab objects 标签里通过查找绿色小点的方式来定位这个bucket variable。
iReport为group创建新的header cells是很简单的,这个操作不需要添加任何新的额外元素到crosstab里。你可以根据你的喜好来填充cells,最好的方式是通过拖曳把bucket从crosstab objects列表拖到新的单元格里。
在上图中,你可以看到加在一个新加的嵌套在column group里四个新添加到crosstab里的单元格,group header单元格,group total header单元格。
你可以使用”up”和”down”按钮来更改你的group的嵌套顺序。
下图中所示的crosstab就是在图16.14在SEX的group被向上移后的效果。
图16.16 MONTH group向上移后的效果
每一个行和列的交叉处定义一个单元格,我们有header单元格,total单元格,detail单元格和一个可选的“when-no-data”单元格。单元格可以包含一个类似于textfields,statictexts,rectangle,images之类元素的集合,但是他们不能包含一个SubReport, 一个chart,或者一个crosstab。
你可以去修改每一个单元格的背景色和单元格的borders:移动鼠标在一个单元格上并且按右键在弹出的菜单中选择“cellproperties”打开单元格属性编辑窗口。
从这个对话框中你可以修改单元格的外观。
如果一个crosstab不能包含一些数据,你可以选择打印别的东西来替代。例如,你可以包含一条消息,方法是,你必须编辑“when-no-data”单元格。在crosstab上右键勾选弹出菜单中的“when-no-data default cell”复选框。这个操作完成后将会出现一种很明显的编辑模式,你可以操作“when-no-data”单元格的内容。
“when-no-data”单元格和crosstab元素有相同的尺寸。如果没有数据展示的话它将替代crosstab打印。单元格橙色的边框标志出你可以编辑的单元格区域。可以在这个单元格里添加一个或多个元素。
在一个crosstab里,所有的元素关联一个单元格,如果一个元素在单元格外面或部分在单元格外面,这是不正确的放置方式并且当编译的时候jasperreports将会抛出异常。iReport在界面中标识这个错误会以红色的框显示。
在crosstab里每一个元素的表达式(如textfield 表达式)可以包含唯一的measure。在这种情况下,你可以直接使用fields,variables或parameters。
一个measure和variable一样是一个对象。要建立一个measure,请打开crosstab属性窗口(在crosstab编辑器上右击,选择crosstabproperties)并且到measure标签页。
点击Add按钮,创建一个新的measure。Measure的属性窗口将显示,你最少应该去设置它的name,class type和expression。在measure里你可以使用fields,variable和parameters
一个measure可以存储一个来自你代码中的实际的值(将calculation type设为nothing)或者一个类似于用count,sum等计算出来的值。
对于每一个measure 你可以提供一个自定义的incrementer类(一个继承自
net.sf.jasperreports.engine.fil.JRIncrementerFactory
的类)去实现特定的increment 方法。
这里的“percentage of”选项允许你选择表现变量最终值的百分比。
你可以提供一个特殊类来计算百分比的值,此时这个类必须实现net.sf.jasperreports.crosstabs.fill.JRPercentageCalculator接口。
在主设计窗口中双击crosstab元素将打开element properties窗口,点击crosstab标签。
Repeat column headers : 如果勾选,当crosstab完全添加到页面中时列的header将会被重复打印。
Repeat row headers : 如果勾选,当crosstab完全添加到页面中时这个行headers将会被重复打印。
Column break offset : 当crosstab超过页面宽度时它指定上一块的数据与下一块之间的空白间隔。
你可以使用元素属性窗口里的crosstab parameter表格建立一个crosstab parameter并且设置一个crosstab的parameter同时为其设置一个默认的表达式(图16.22),要添加一个parameter,请点击Add按钮,这个crosstab parameter 窗口将会出现。
对于一个parameter,你必须为其指定name,class type,同时你还可以为其指定一个可选的可以包含主报表中任意一个fields,variable,parameters的表达式。
警告:使用dataset的过滤查询,你必须用定义在crosstab properties窗口里的dataset 启动parameters,不能用crosstab里的parameters.
你可以在crosstab objects窗口里看到crosstab parameters的列表,他们用红色的点标记出来。
你可以在启动时用一个MAP来设置crosstab parameters里声明的值。在这个用例中,你需要在element properties窗口里提供一个合法的parameter mapexpression。
你可以用主报表或一个subdataset来填充一个crosstab。在后面的例子中,在crosstab properties窗口“crosstab data”标签里你必须指定要启动的dataset。
如果你的数据是pre-sorted.你可以勾选“Data is presorted”的checkbox以使得填充数据的处理速度加快。
Reset type/group和increment type/group选项可以用来定义当集合类型的数据需要重置并且当添加一条记录到你的dataset。
关于怎么设置dataset的启动参数请参考subdataset的相关章节。
Crosstab variables 是内置的对象,可以结合数据在不同的聚合水平使用在crosstab textfield的表达式里。
对于每一个尺寸,jasperreports创建variables根据每一行或列存储measure的合计值。
下面的例子是一个用来显示不同学历不同部门的员工数的报表.
这章我们将处理一些我们在使用iReport和JasperReport过程中遇到的常见问题。他们是iReport论坛上常见的问题,但JasperReport官方文档也没有清楚的解决方案的。
经常需要在报表上显示一个字段的值和与这个值有关的百分比。考虑表1中的这几行。
|
|
|
香蕉 |
10 |
20% |
橘子 |
25 |
50% |
草莓 |
15 |
15% |
表格1
这个例子的百分比插入在C列,它表示B列中的值所占的百分数。用以下公式计算得出:
C = (ΣB*100) / B (1)
ΣB =所有B列的值的和
尽管这个公式看起来很容易就算出了百分比,但公式(1)不适合在JasperReports中计算。这是因为公式使用的值是无效的在公式被使用时;具体来说ΣB是获得报表最后的值,就是当所有的记录被处理以后。然而,你要使用的B的值仅仅在计算(1)记录结果时。这儿没有简单的方案为这个问题。唯一的计算总数的方式就需要最终的百分比(先前例子中的ΣB )。当你预先计算你需要的值,你可以将它作为参数传递给报表。公式(1)就将变为:
new Double(($P{TOTAL_OF_B}.doubleValue()*100)/$F{B}.doubleValue())
TOTAL_OF_B代表包含你计算的值的参数
每个组在JasperReports中和一个表达式关联。在报表生成期间,表达式的值一改变,一个新组开始发生一些事情。有时必须计算组的数量(计算组的值改变了多少次)。照这样做,需要一个变量:我们将命名为GRP_COUNT;基本思路就是仅仅当变量
图16.1
图16.1中的例子用到的查询:
“select * fromcustomers order by country” 和COUNTRY组的表达式:$F{COUNTRY}.
声明变量GRP_COUNT :
Class: java.lang.Integer
Calculation type: Count
Reset type: Report
Variable expression: (($V{COUNTRY_COUNT}.intValue()== 0) ? "" : null)
这个表达式是说:如果你计算当前组的实例的第一条记录(记录号为0),然后返回一些非空的东西,也返回null值。自我们选择count作为计算类型,
变量GRP_COUNT将增加仅仅当表达式的值非空时,就说当当前组的第一条记录被计算时。
图16.2
显示这个值到textfield,你设置元素表达式的赋值时间到这个组来计算发生次数,在我们的例子中使用COUNTRY组(图16.3)。
图16.3
图16.4
图16.4可以看到打印结果。在空白框中显示了组号。
如果evaluation time设置到“Report”, 组发生的总次数就将被打印出来。
有时需要将detail分开成更多的bands。主要是为了在一些情形发生时在一个指定的detail band中用另一个来替代。这些情形必须用band PrintWhen Expression 测试过。可能有任意数量的detailbands创建新组,这些新组用一个组表达式,这个表达式在每条记录后改变值。你可以用来作为记录的关键字段表达式或一个计算变量。使用这种方式,此外在detail band中,你将有每条记录的group header和group footer,不能用同样的方式使用这个detail band。
图16.5可以看到报表的设计,detailband被隐藏到一对新组的header bands的剩余空间,这个使用一样的组表达式:这条记录关键字。
选择选项为第一组,我们能一页打印一条记录。
图16.5
JasperReports不提供任何方法来插入一个页和band合用。意思就是说你在一个页上呈现多个detail。下面的例子中将分detail在两页上:第一页我们想显示客户的名字的代码,第二页打印客户地址。
完成这个例子,我们将打印每条记录在一个新页上:这种方式是我们期望在一页上有一条记录在两页的内容。
让我们一步一步来,首先我们将创建一个组在一个空白报表上,我们指定组的表达式$V{REPORT_COUNT}和检查组标“Start on a new page”:我们用这种方式得到每条记录的在同一页上。现在我们去分开detail。接下来我们使用一个没有任何连接和datasource(我们设置子报表的连接属性为“Don’t use connection or datasource”)的子报表。准备创建这个子报表通过一个简单空白文档,且设置margins为0。
图16.6
也设置所有的bands的高为0(title band和summary band除外)。在我们用做子报表的报表属性选择选项“Title on a new page”和设置“When no data”值为AllSectionNoDetail 。
所有来自与主报表字段的值将作为参数传递;所以我们声明所有需要的参数。我们例子中需要四个字段:
- CUSTOMERID
- COMPANYNAME
- CITY
- REGION
那么放所有你需要的textfields到报表中。请注意所有插入到title band中的元素被打印在第一页,另外,放在summary band中的字段将在第二页被打印。返回到主报表,将字报表插入到detail band中。
我们填充子报表参数用正确的表达市在主报表中(更准确的说,我们填充子报表参数表在“Subreport(Other)”选项卡,查看图16.8)。
编译主报表和子报表并运行打印,如果一切ok,将看到类似图16.9的结果。
JasperReports不支持交叉报表,指报表的行和列都变化的报表。
以下表格是一个交叉报表的记录,显示了所有有效年代的生产记录:
|
|
|
|
|
900 |
950 |
1000 |
|
400 |
500 |
600 |
年代不可预言的,取决与取到的数据。
这种情形,最常用的方法为这种报表就是去固定一个列的最小号显示,手动填充一个datasource(例如JRJavaBeanArrayDataSource)来组织每行的值。这种方式我们很难想象到将出现的数据。
如果供应许多的列比你需要的(例如,报表能显示后5年的信息,但是你仅仅有前3前的信息),你的工作就是加一个假值做为字段值替代null或空白字符或短字符(-)。
有时你需要重新找回更多的数据比在单独一个datasource,在同一时间。去达到这个,解决方案依赖你严密的想法。通常执行多个问题的方法就是使用子报表。此时你可以说点什么关于我们必须连接子报表,你能设置不同的表达式为每个子报表。你可以使用两个连接中的一个作为参数传递给报表。
如果不能分别找回数据通过不同的datasource用子报表,你得实现一个“lookup”方法,就像用一个静态类,或者添加它到报表脚本中。Lookup方法可以重新找回数据从任意的datasource。
以下的例子表现了一个简单的lookup类。用这个类去解码一个国家的名字给一个国家的代码。这个方法被传递一个已经打开的JDBC连接和代码作为参数去解码
Public class LookUp {
public static String (java.sql.Connectioncon, int code)
{
java.sql.Statement stmt = null;
java.sql.ResultSet rs = null;
try {
stmt = con.createStatement();
rs = stmt.executeQuery(
"select STATE_NAME from STATES wherecode=" + code );
if (rs.next())
{
return rs.getString(1);
}
return "#not found!";
} catch (Exception ex)
{
return "#error";
} finally {
if (rs != null)
try { rs.close(); }
catch (Exception ex2) {}
if (stmt != null)
try { stmt.close(); }
catch (Exception ex2) {}
}
}
}
调用这个方法,传递作为不同数据连接,你可以得到你想要的数据从不同的数据库。
JasperReports不支持使用存储过程来找回数据去打印。为避免这个局限性,你可以在运行报表之前执行存储过程。
重新找回数据用存储过程能****入在临时表格中,你可能执行一个查询,或者如果他们很少,你可以把他们作为参数传递给报表。在最后,这个临时表将被删除。如果你的存储过程可以返回一个ResultSet,你可能包含它在一个datasource用JRResultSetDataSource
iReport的核心就是一个开源的JasperReports库,它是由一个罗马的叫Teodor Danciu的开发者开发。是目前最有利于创建分布式和强有力的报表的免费软件库。这章我们就来阐述一下JasperReports的基础概念来加深我们对iReport工作流程的理解。
JasperReports API, 为报表定义的XML结构语法,以及在我们程序中需要用的库的详细资料都归档在这本名叫“TheJasperReports Ultimate Guide.”的手册中。这本书非常的超值。(目前标价35$).其他可以直接利用的信息和例子可以到官方网站: http://jasperreports.sourceforge.net。
两者不相同的是,iReport的分布式依照GPL规范,而JasperReports依照LGPL规范,因此它的局限性很小。这就意味着JasperReports能够被随意应用于商业,不用购买昂贵的软件许可或呆在家里从网上搜索复杂的开源的许可。这是用iReport创建一个被用做商品报表的基本原则,事实上,程序仅仅需要用JasperReports库来打印出报表来,仅仅在程序运行时用到它。由于没有完整的商业许可(指需求以外的许可),iReport只能作为一种开发工具,也不可能成为一个不符合GPL分布式标准的程序的一部分。
报表的生命周期和java类的生命周期非常的相似。Java由一些源文件来组成,也就是它可以依照它的语言规则进行自身的扩展,编写。这些源代码被编译器编译成扩展名为class的文件。可以通过接口来实例化或直接加载到内存中调用;执行期间这些属性将被加强。
同样的,一个报表也是由一个源代码的文件来描述,这个源文件就是由DTD(jasperreport.dtd,version 0.6.3is listed in Appendix B)来定义的XML标记。在0.5.3版本中它的源文件扩展名变成了.jrxml(例如:JasperReportsXML); 取代了一般的.xml扩展名。报表的源代码被编译成jasper文件(扩展名是.jasper)。jasper文件是一种预报表,严密的说就像是java的类被封状成的对象。Jasper文件通过你的应用程序来加载。它被添加一个数据源的标记从而创建报表,接着它就能以你想要的格式输出(例如:pdf或xls)。
因此它可以被定义成两种截然不同的动作:一个是用于执行在开发期间(设计和编写一个报表,编辑一个jasper源文件),另一个是用于执行在运行期间、
(加载开发期间的jasper文件生成报表)。
已经解释过,一个报表就由一个被创建的DTD文件定义的XML文件 (jasperreport.dtd)。这个源文件是由一系列的片段定义,这些片段就是一些关于报表的物理特性,像这个页面的尺寸,版面的布置,以及版块的高度等等;还有一些逻辑上的特性,像参数,变量,一个为得到被选择的数据的查询的定义等等
简单的说,这个jrxml源代码包含的片段就是下面这些:
Report main characteristics |
Property (0,+) |
Import (0,+) |
Global font (0,+) |
Parameters (0,+) |
SQL query (0,1) |
Fields (0,+) |
Variables (0,+) |
Groups (0,+) |
Group header |
Group header elements (0,+) |
Group footer |
Group footer elements (0,+) |
Predefined bands |
Predefined bands elements |
jrxml文件例子:
"http://jasperreports.sourceforge.net/dtds/jasperreport.dtd">
name="untitled_report_1"
columnCount="2"
printOrder="Vertical"
orientation="Portrait"
pageWidth="595"
pageHeight="842"
columnWidth="266"
columnSpacing="0"
leftMargin="30"
rightMargin="30"
topMargin="20"
bottomMargin="20"
whenNoDataType="NoPages"
isTitleNewPage="false"
isSummaryNewPage="false">
mode="Opaque"
x="145"
y="6"
width="245"
height="34"
forecolor="#000000"
backcolor="#FFFFFF"
key="element-1"
stretchType="NoStretch"
positionType="FixRelativeToTop"
isPrintRepeatedValues="true"
isRemoveLineWhenBlank="false"
isPrintInFirstWholeBand="false"
isPrintWhenDetailOverflows="false"/>
verticalAlignment="Top"rotation="None" lineSpacing="Single">
pdfFontName="Helvetica"size="24" isBold="false" isItalic="false"
isUnderline="false"isPdfEmbedded ="false" pdfEncoding ="Cp1252"
isStrikeThrough="false"/>
pattern=""isBlankWhenNull="false" evaluationTime="Now" hyperlinkType="None">
mode="Opaque"
x="1"
y="1"
width="264"
height="18"
forecolor="#000000"
backcolor="#FFFFFF"
key="element-2"
stretchType="NoStretch"
positionType="FixRelativeToTop"
isPrintRepeatedValues="true"
isRemoveLineWhenBlank="false"
isPrintInFirstWholeBand="false"
isPrintWhenDetailOverflows="false"/>
verticalAlignment="Top"rotation="None" lineSpacing="Single">
pdfFontName="Helvetica"size="10" isBold="false" isItalic="false"
isUnderline="false"isPdfEmbedded ="false" pdfEncoding ="Cp1252"
isStrikeThrough="false"/>
class="java.lang.String">
清单3.1
图3.1展示了清单3.1打印出来的结果。其实清单3.1是iReport的加长型,它比我们需要的太长拉。这是因为iReport不能生成精短的代码(例:不能一些具有缺省值的属性)。
图3.1
不管如何减少XML代码,也无法改变报表的最终结果和生成速度。
在编辑(通过一些JasperReport类来实现)jrxml文件期间,这个xml文件被分解加载为JRBaseReport对象。JRBaseReport一个丰富的数据结构,可以将xml的内容精确的放到内存中。所有的解析表达式被加载,生成它相应java类文件。这些类继承JRCalculator,依靠普通java编译器编译成的这些类被加载为缓冲字节。开始初始化JRBaseReport,一个JasperReport类(继承JRBaseReport类)被实例化,先前的JRCalculator类缓冲字节被存储在新类的compileData区域。从而获得的JasperReport类连续的写进jasper文件,并随时准备被加载。
JasperReports的速度取决于实际情况,所有的报表规则被编辑到java的本地字节码中。报表的结构在编辑时被检验,而不是在运行时。
通过其他方式提供数据来完成打印是不可能的,这些最经久耐用,吸引人的报表是没有意义的。Without the possibility of filling a print through some dynamicallysupplied data,the most sophisticated and appealing report would be useless.
JasperReports 允许通过两种不同的方法来提供指定的数据来打印:通过参数和数据源,这主要依赖一个普通的接口JRDataSource.
图3.2
一整章的内容都是关于datasources,哪儿将解释它如何应用在iReport中和如何能定义一个符合常规的datasources。(为了能提供一个准确的JasperReports)。JRDataSource能读取一组被整理在表格中的记录体(行和列)。
JasperReports可能被报表用任何地方,来代替一个直接的datasources,一个JDBC(已经举过例子)能连接任何一个关系型数据库,对指定的一个报表执行sql查询。 如果数据(通过datasources)不足够,或者它需要指定特定的值完成执行,它将很有可能产生一些名值对传送给打印机。这些对被叫做parameters(参数),他们可以被预先性的传给报表。通过fillManager能够加入一个jasper文件和datasource在一个JasperPrint 对象。 通过实现JRExporter接口的类这个对象能够创建一个真实的打印以你所期望的格式输出。
JasperReports 可以由你做主来选择不同格式的输出,用来创建pdf,xls,cvs,xml,html,等文件。通过JRViewer类能够直接打印你屏幕上所看到。
当一个新的版本的JasperReport被发布,通常一些类会有所改变。这些改进的类特意地定义报表的结构。所以为了避免在编译报表时因用了不同的库而产生的冲突,JasperReport使每个编译过的jasper文件具有联系性,可以精确的判别库版本并使用老版本进行编辑。如果你执行一个打印加载一个jasper文件,它不同于老版本支持的文件,就会产生一个错误。可能是这种情况:
java.io.InvalidClassException:
net.sf.jasperreports.engine.base.JRBaseReport; localclass
incompatible: stream classdesc serialVersionUID = 406, localclass
serialVersionUID = 600
net.sf.jasperreports.engine.JRException: Error loadingobject from
InputStream
Caused by: java.io.InvalidClassException:
net.sf.jasperreports.engine.base.JRBaseReport;local class
incompatible: stream classdesc serialVersionUID= 406, local class
serialVersionUID = 600
然而,老的报表源可能被编译用新版本的库,这个源代码被第一次编译:这是因为锌版本的通常仅仅传入新的标签但又不是必须的,并没有修改XML通常的结构。
移植一个JasperReports到下一个版本下完全可以的。它能够很快的被执行由于一个叫massive compiler 的iReport插件,它能够编辑所有在同一目录结构下的报表,能安全的拷贝已经现有的jasper文件。
我们将讨论massive compiler 在插件章节。
所有的JasperReports的规则被定义通过表达式。一个表达式符合java语法,即有一个对象和结果。
表达式的例子:
• “This is an expression”
• new Boolean(true)
• new Integer(3)
• (($P{MyParam}.equals(“S”)) ? “Yes” : “No”)
无效的表达式例子:
• 3 + 2 * 5
• true
• (($P{MyParam} == 1) ? “Yes” : “No”)
详细的查看第一个和第二个例子都是无效的,因为他们都是简单类型(int 在第一个事例,boolean在第二个事例)。第三个表达式也无效的,因为采取MyParam参数(之后我们将定义为$P{...}格式) 是一个简单类型,它能被比较通过==和一个int型,但结果不是true。
表达式返回的类型是由上下文决定的。举个例子如果一个表达式恰好终止当一个元素被打印之后,这个返回类型将是boolean型。同样地,如果我写一个加了下划线的number型字段,返回类型就是Integer或Double。
在表达式中我提到了参数,变量和字段,报表定义的语法概要:
|
|
$F{name_field} |
指定字段名 (F means Field) |
$V{name_variable} |
指定变量名 |
$P{name_parameter} |
指定参数名 |
$P!{name_parameter} |
这是一种特殊的格式,是报表查询的一部分,这个参数肯定不会负一个值传递给预编译语句,但它是这个查询的一部分。 |
表3.2
字段,变量,参数永远作为一个对象(他们可以负null值),他们的类型在被声明时被指定。JasperReports0.6.2版本以后,出现了一个新类型$R{name_resource}结构。它被用做字符串的本地化。(我们将在国际化那章来讨论)
通常定义一个返回对象用一个表达式是不够的。例如:如果你想打印一个罗马字体的数字,或者用一个特别的数据代替周日,就可能要将详细的细节传给一个外部类方法,这个方法应该声明为staitic,例如:
MyFormatter.toRomanNumber($F{MyInteger}.intValue() )
toRomanNumber 是 MyFormatter类的静态方法,带有唯一的int型参数并传回这个罗马版的数字in a lace。
很多地方都用到这个技巧,比如推断出一个CLOB区域的文本内容,或者添加一个值到HashMap参数中。这种操作就不能依靠简单的表达式。
我们用一个简单的程序(清单3.2)来完成这章关于JasperReports提出的介绍。这段代码将展示如何由一个jasper文件生成一个pdf文件,这里用一个特殊的datasources JREmptyDataSource.JREmptyDataSource 是一种空的datasources。我们引用test.jasper文件(清单3.1)来进行编译。
import net.sf.jasperreports.engine.*;
importnet.sf.jasperreports.engine.export.*;
import java.util.*;
public class JasperTest
{
public static void main(String[] args)
{
String fileName ="/devel/examples/test.jasper";
String outFileName ="/devel/examples/test.pdf";
HashMap hm = new HashMap();
try
{
JasperPrint print =JasperFillManager.fillReport(
fileName,
hm,
new JREmptyDataSource());
JRExporter exporter = new
net.sf.jasperreports.engine.export.JRPdfExporter();
exporter.setParameter(
JRExporterParameter.OUTPUT_FILE_NAME,
outFileName);
exporter.setParameter(
JRExporterParameter.JASPER_PRINT,print);
exporter.exportReport();
System.out.println("Created file:" + outFileName);
}
catch (JRException e)
{
e.printStackTrace();
System.exit(1);
}
catch (Exception e)
{
e.printStackTrace();
System.exit(1);
}
清单3.2
这章我们将分析报表的构成,来观察一下它由那几部分构成,当用数据打印时这几部分是如何运转的。
一个报表依靠“type”页来定义说明。它被分成不同水平的一份被叫做bands。当报表加入数据去打印时,这些部分被印刷很多次依照他们自身的属性(依照报表创建者设计的规则)。举个例子,page header 被重复的打印在每页的开始部分,这样的话,每个band都会重复的打印每一单一的记录。
这个 “type” 页被分成9个预先确定的bands作为新组被加进去。事实上,iReport控制每一个头条(Group header)和一个摘要条(Group footer)为每一个组。
图4.1
一个带总是和页的宽度一样(左右的空白除外)然而它的高度,即使已经被设计好,但可能还会依据被包含在内的元素的实际来打印;它能够自由的延长靠近页底。这种特色的出现在bands包含一个subreports或者text field时纵向的内容。通常情况下,bands的高度被用户指定一个“minimal height”。不是所有的bands能依照内容被这个外力改变,特别是:Column Footer,Page Footer 和 Last PageFooter.所有的band高度的总和(除了background)总是小于或等于页的top和bottome之间的最小高度。
这是首先看到的band。它被创建仅仅一次,可能被打印在每一分开的页。耍点小聪明,你能利用这个模拟一个无效的subreports(随后我将看看如何做)。关于尺度的确认,它是不可能躲过去的,在报表页的高度设计期间。As regards the admitted dimensions, it is not possible to exceed,
during design time, the report page height (包含top和bottome标记之间)。如果title被打印在几张单独的页面上,这个band的高度不用被包括在整个band的高度之内的,一般低与page的高度。
这个band允许你设计一个page的header。它的高度在设计阶段被定义,通常不会改变在创建报表的过程中(除非有垂直插入相当大的成分,例如这个textfields包含很长的文本和subreports)。页面的header出现在整个打印页的相同位置在设计阶段期间。Title和Summary bands不包括page header 在打印一个单独的页时。
这个band在每一个详细的列开始时被打印。这column的概念一会儿再结实。通常的,包含标签的tabular报表的列的名字****入在这里。
一个报表能容纳零个到多个group bands,它允许收藏详细的记录在不动groups。有group header ,自然要有group footer(他们能能独立的展现和取消)。不同的属性被联合在一个group。他们确定他们的行为功过查看 graphic point。它是可能的影响一个新的页的group header或者一个newcolumn,打印这个band在整个页的下面,如果这个bands超出了一个单独的页(以一个组的级别作为一个page header)。它 是可能的去修改被请求的高度并打印它:如果它是超过其他的高度,这个group将被打印在一个新的page(特别要注意它的值由于太长,在打印时会无限的循环),等等。接下来我们将详细讨论groups。
Detail band 将每个通过datasources读到记录响应的传给打印机。很可能,大多数的元素都被放在这里。
这个band表示一个group的结束。通常它包含的区域显示小计或者分开图解元素,作为一条线,等等。
这个band的出现表示每一个column的结束。它的尺寸是不能被调整的(即使它包含一个相当大的元素,像subreports或者文本域)。
这是页脚。它和page header同时出现在每页。同column footer一样,他是不足够大的。
如果你想使最后一页的footer与其他的页的footers不一样,这时可能就要用到这个band。如果这个band的高度是0,他将被完全的忽视,其他确定结构的普通页也能适用在最后一页。它首次出现在JasperReports的0.6.2版本。
在其他的地方这个组名被叫做report footer。它允许你在report的最后插入你想要的任何内容关于总结等。
Band第一次出现在JasperReport0.4.6版本时。它由于许多用户的坚持不懈的要求能够创建水印和similar effects而引入 (整个页面的结构). 它可以具有最大的高度以至于和页面高度一样。
现在我们已经看过报表的单个部分,我们将继续创建一个新的报表。按菜单上的这个按钮选择New Document:打开一个窗口。在这里填写报表的属性(图4.2)。这个窗口可以随时打开通过view菜单的Report properties。
图4.2
报表的名字是第一个属性。这个名字是本地命名,它和源文件的名字是相互独立的,它仅仅被JasperReports library调用(例如:命名一个报表编译后的java文件)。
页面的尺寸大概是报表最重要的属性。一系列报表的尺寸标准被提议。iReport和 JasperReports使用的最小量度单位是像素。(像素大小是75点每英寸,每英寸的点数)。
然而,我们经常使用的报表的量度单位更为平常,像厘米,英寸或毫米。
表4.1列出了量度标准和像素大小。由于尺寸管理基于像素,所以当我们用不同的量度看同一数据会发生很大改动。
|
|
LETTER |
612 x 792 |
NOTE |
540 x 720 |
LEGAL |
612 x 1008 |
A0 |
2380 x 3368 |
A1 |
1684 x 2380 |
A2 |
1190 x 1684 |
A3 |
842 x 1190 |
A4 |
595 x 842 |
A5 |
421 x 595 |
A6 |
297 x 421 |
A7 |
210 x 297 |
A8 |
148 x 210 |
A9 |
105 x 148 |
A10 |
74 x 105 |
B0 |
2836 x 4008 |
B1 |
2004 x 2836 |
B2 |
1418 x 2004 |
B3 |
1002 x 1418 |
B4 |
709 x 1002 |
B5 |
501 x 709 |
ARCH_E |
2592 x 3456 |
ARCH_D |
1728 x 2592 |
ARCH_C |
1296 x 1728 |
ARCH_B |
864 x 1296 |
ARCH_A |
648 x 864 |
FLSA |
612 x 936 |
FLSE |
612 x 936 |
HALFLETTER |
396 x 612 |
_11X17 |
792 x 1224 |
LEDGER |
1224 x 792 |
表4.1
通过修改高度和宽度,你就可以创建任意大小的报表。报表页的定位有landscape和portrait选项,事实上这并没有太大的意义,因为报表页是由宽和高来刻画它的尺度的,并且每个单页都是相互独立。然而这个属性可以别用来确定报表的输出。
页面的空白尺寸依赖于Margin选项卡的四个输入值来确定。
像你看到的那样,一个报表页被几条平行线分开为几部分:bands。这个选项页是用来排版报表的,现在的这部分具有独立的数据(像title片段,或者page footers),和其他的片段一样被打印根据数据记录的不同从零开始(像groupheaders和detail)。这个选项页的最后一个属性值能分开垂直的列为了利用报表页中可利用的空间。
上下文中的column的概念很容易被”field”搞混。事实上,column和记录集是没有关系的,只和detail band有关系。意思就是说一条记录用十个fields和一个表的话,不需要十列。然而,这些元素可以用table实现恰当的放置。十列将导致一个很长的记录列(非常狭窄的横条)被打印。下面的例子将告诉你怎样设置值为一个简单的列报表在A4纸上。
图4.3
Columns的值是1,它的宽度和整个页面一样,除了空白。列之间的空间没有意义,所以它的值是0。
图4.4 图4.5
看4.5图,这个页的大部分没有被使用,如果使用大量的列,这个报表看起来会好看一些。图4.6展示了两列报表的尺寸大小。
图4.6
既然这样,在这个“columns“,我们输入2。iReport将自动的计算列的宽度依照页的空白和宽度。如果你想在两列之间插入空白,那就为”spacing”填写一个值。
像4.8图中看到的,这个页面的空白有被更好的利用。
滥用列的话可能将打印一个非常长列(像一个电话本一样)。总之,当detail band和连续的bands出现超过一列的宽度空白时你要记住使用列来减少宽度。所有的空白,列的宽度和每列之间的空白,加起来要小于页的宽度。如果不检验这种情形的话可能导致错误产生。
图4.7 图4.8
在图4.5和4.7中页面上有用区域设置报表的元素(像fields,images等等)。这部分是不可利用的,像空白和第一列余下的空白。Theparts which are not utilizable, such as margins and columns following the firstone (which have to be considered like the continuation of this one) arehighlighted in grey.
以上我们看到的仅仅是基础关于设计的基础设置。现在我们将看一些高级选项。其中一些将被彻底的检查和粉肠详细的解释在以下章节,如果要想完全的理解和应用,那就要在努力学习JasperReports的用法一直到精通。
脚本是一个java类,它的方法执行在报表生成期间,通过触发一个特殊的事件,像一个新页的开始,一个group的结束等。我们常常可以看到那些生动的工具图片,像MS Access 或 MS Excel,脚本就相当于一个module,是一些程序和特殊的事件的关联或可返回的功能,在报表的上下文****入。(例如text field的表达式)
将有一个章节全部用来讲解脚本。在scriptlet选项卡(图4.9),可以指定一个外部的脚本(java类)或激活iReport的内部脚本支持。
图4.9
如果你不想使用脚本,那就设置下拉菜单的值为:Don’t use scriptlet class…或者在输入区域填写你所要使用的类的名字。
在”more..”选项卡(图4.10)可以对打印进行设置。
图4.10
Title指定为一个新页的选项,打印在每页的开始,在title之后页面将发生大的变化。图4.12和4.13展示报表的结果,图4.11显示报表。
图4.11
Title被打印总是一样的,而且在每页的顶部。
图4.12显示使用缺省设置打印的结果:
图4.12
图4.13显示打印的结果如果“title on a new page “选项设置为true。Figure 4.13 shows the print result withthe “Title on a new page” option set to true.
像你看到那样,没有一行其他的band被打印在title页,甚至没有pageheader或page footer。然而这页仍然计算这页的总数。
图4.13
这个选项完全和先前的选项一样,只是summary band被打印在最后一页。现在,如果你选择了这项,那新页将包含summaryband。
这个选项允许你加强column footer band的打印,直接在最后detail band(或group footer)后显示而不不在column后显示。当你想用报表元素创建表格时使用这个选项。 (详细请看JasperReports tables.jrxml)
Print order用来决定如何组织打印多列数据。缺省的print order一是vertical,它是垂直的打印记录直到页末开始打印新的一列。 (就像报纸或电话本一样) 。Horizontalprint orderprints 以横向打印记录当一行占据页宽后开始打印另一行。图4.14和4.15:
图4.14 图4.15
这两个图清晰的显示这个两个选项的概念。如你看到的,每个名字按字母的顺序打印。图4.14显示了纵向的打印(第一列打印完接着打印第二列),图4.15显示了横向的打印(打印完所有的行显示出整个列)
当提供一个空的数据打印时(或者sql查询没有返回记录) 一个空文件被创建(或返回是一个零字节长度的流)。这个缺省的行为能被修改通过指定其他的,在没有数据时。(指when no data)。表4.2概述了可能的值和意义。
|
|
NoPages |
缺省值,最后的结果为空。 |
BlankPage |
返回一个空白页。 |
AllSectionsNoDetails |
返回整个页的组成,出了detail band |
表4.2
il8n选项卡用来设置片段的参数。
Resource Bundle base name 是一个参数名,当你想使报表国际化时。Resource Bundle包含用在报表中的标签,句子,表达式翻译文本以指定的语言。这个语言符合一个特殊的文件。base name
表示文件名,能帮助你找到这个文件用正确翻译。为了用精确的语言来重建文件名,一些language/country首位字母大写(e.g._it_IT,forItalian-Italy)和.properties扩展名作为前缀。我们将解释国际化在11章。
保存源文件的缺省编码格式是UTF-8. 然而,如果你想用一些你需要的XML编码格式,你就需要指定正确的charset(UTF-8)。
The UTF-8 manages all accented letters andthe euro.其他常用的可以选择编码格式列表(“ISO-8859-1” 广泛应用于欧洲).
这章我们将解释报表的主要对象和他们的特性。
这里的”element”就是一些图形对象,像一些文本或长方形。不同于字处理机那样,在iReport那段的概念中,表和分页符将不存在;任何东西被创建借助于elements,包括文字,当他们排成一行时就创建了表格,等等。这些相近的被大多数报表工具所采用。
“basic”元素中有7个由JasperReports提供:
- Line
- Rectangle(长方形)
- Ellipse
- Static text
- Text field (or simply Field)
- Image
- Subreport
通过这些元素的结合,能够创建任何一种报表。
除了这些之外,iReport还提供了两种特殊的基于象元的元素:chart(图表)和barcode(条形码)。
每种元素都有一些普通的属性,像高,宽,位置,band应该被放的位置和其他特殊的属性(例如:字体,在长方形的情况时,border的厚度)。它可能分为两个大的类别的组:图形元素(像:line,rectangle,ellipse,image)和文本区域(labels和textfield)。
子报表代表一种独立的元素,因为他使用起来很复杂,我也将接触他们在一个单独的章节。
这些元素****入bands。特别是每一元素都不能合放在一个band中。如果这些元素不能被完整包含在band中,报表编译器将返回一个错误信息,关于元素的错误的姿态;不管怎么样,报表都将被编译,甚至是更糟糕的情况,但”out of band”将不能被打印出来。
为了添加元素到报表中,选择(图5.1)工具条上目前显示的工具
表格5.1
取向工具被用来对这些元素做更重要操作和一些常用的工具。当你想插入一个元素时,点鼠标左键在你想插入元素的地方向下拖拽鼠标来画一个合适的长方形。当松开鼠标时,一个新的元素将被自动的选择和创建。这个新的元素也将在iReport面板的右边的元素浏览(elements browser)中出现。
图5.1
双击鼠标右键或者选择菜单View→Element Properties,it 能够打开元素的属性窗口(图5.2)。
图5.2
这个窗口是有组织的几个选项卡。This window is organized in severl tabs. The“Common” tab 选项卡包含了每一种元素的通用的属性,其他的选项卡是每一种元素单个的属性。
能够选种多个元素在同一时间功过取选工具和画一个长方形将你要选择的元素包含在内。被选择的区域会显示一个粉红色的长方形框。
或者,选中一个或多个元素在同一时间,通过按“Shift”键用鼠标点中你已经插入的元素。
选种一个元素时将在该元素长方形的每个角显示兰色的小方框(图5.4),当选择多个元素时该元素长方形的每个角显示灰色的小方框。(图5.5)
图5.3 图5.4 图5.5
多重选择中的第一个元素作为主要的选择。.
为了能够调整元素的大小,可以移动鼠标的光标到长方形的边上或拐角处:点并拖拽来安排你想要的尺寸。为了更小心的调整,可以在元素的属性框的特别选项卡中填写合适的尺寸。如果同时选择了多个元素,一个元素的调整会调整所有的元素。
此外,你也可以用放大报表,通过使用工具条上的zoom工具(图5.6):
图5.6
你可以在选择框中选择百分比(图5.6)或者直接写一个你想要的值(明确一下,字符后使用’%’号)。
警告:在调整元素期间,必须注意使用整数(像1x,2x,3x,等)来避免近似值的错误!
一个元素可以通过鼠标来移动:通过点这个元素,选中后可以进行拖拽到任意你想要的位置。为了能够最大化的提高移动的准确度,你可以用方向键进行移动,一次移动一个像素。同样的操作,你可以完成通过按shift键,将引起这个元素十个像素的移动。
.即使移动时有一点阻碍,也可以破坏掉这个元素的阻碍,可以选择菜单Edit→Disable elements mouse move.
菜单可以View→Show grid查看不同元素的位置,通过格栅作为参考。也可以插入一个动作,通过菜单Edit→Snap去对起格线。
图5.7
随着越多元素组织进报表,可能就要用到几个工具,这些工具可以从这两个菜单format和contextual中找到,可以按右键查看选择插入元素。更多的官能性需要选择更多的元素。在这种情况下,基本元素被作为做作的参考。表5.2是对官能性的总结。
|
|
|
Align left(左对齐) |
校正基本元素的左边 |
√ |
Align right(右对齐) |
校正基本元素的右边 |
√ |
Align top |
校正基本元素的上边(或者上部分) |
√ |
Align bottom |
校正基本元素的底边 |
√ |
Align vertical axis |
校正被选中元素的水平中心 |
√ |
Align horizontal axis |
校正被选中元素的垂直中心 |
√ |
Align to band top |
设置最高值为0 |
|
Align to band bottom |
尽可能的放置元素在属于他的底边的band. |
|
Same width |
设置被选择元素的宽度为一样 |
√ |
Same width (max) |
设置被选择元素的宽度都为最大宽度 |
√ |
Same width (min) |
设置被选择元素的宽度的最窄值 |
√ |
Same height |
设置被选择的元素的高度等于基本元素的高度 |
√ |
Same height (max) |
设置被选元素的高度等于最高元素的高度 |
√ |
Same height (min) |
设置被选元素的高度等于最矮元素的高度 |
√ |
Same size |
设置被选元素规格为基本元素 |
√ |
Center horizontally(水平居中) (band based) |
设置被选元素水平居中 |
|
Center vertically(垂直居中) (band based) |
设置被选元素垂直置中 |
|
Center in band |
字段中间 |
|
Center in background |
整体置中 |
|
Join sides left |
被选元素以靠最左边元素为基准,所有元素的左边相连 |
√ |
Join sides right |
被选元素的以靠右边元素为基准,所有元素的右边相连 |
√ |
HS →Make equal |
使被选元素之间保持一样的距离 |
√ |
HS →Increase(增加) |
在所有元素中见插入5个像素的空白(向右移动) |
√ |
HS →Decrease(减少) |
在所有元素中间插入5个像素的空白(向左移动) |
√ |
HS →Remove |
删除水平方向的空白并向左移动元素 |
√ |
表格5.2
元素能够被互搭;也就是通过使用格式功能Bring to frontand Send to back使元素置前或置后。zorder被给通过整齐的元素插入到报表。这个排列能被看到在元素树,这些元素被查看在每一个band从最矮到最高的。
我们说过,一个元素已经被连接到所属的band。如果这个元素的一部分或全部超出band,它将会出现一个红色的高亮框架显示(仅仅为text元素),这个位置可能就是无效的。其他元素,这个错误位置被高亮显示在元素树或者被选择的元素上用一个红色的拐角。
图5.8
如果一个元素的部分遮掩了其他元素,这个拐角用绿色高亮显示;但如果一个元素的全部遮掩了其他元素,这个拐角用粉红色高亮显示。
图5.9
移动一个元素从一个band到另一个band,iReport可以自动改变所属的band。去指定不同的band,需要用属性窗口的第一个复选框(图5.9)。
如果两个或更多个元素被选择,那么属性窗口将只显示公共属性,如果这些属性值是不同的,将显示空值(使用的字段被显示空白)。具体的值为特殊属性,将适用于所有被选择的元素。
元素树能够容易和精确地局部地选中报表元素。
图5.10
这里报表的外型是一个属性结构:主节是bands(使用一个文件夹表示),进到band里面是元素符号,名字和坐标能被看到,他们也被置于band中。
双击一个你选择的元素,将打开该对象的属性窗口。也可以通过鼠标右键属性菜单打开。再往后常用的功能就复制和粘贴,这儿有两个特殊的功能,move upand move down, 意思就是说可以修改一个元素在具体band中的z-border.
图5.11
如果选择了band的名字,可以右键属性进行对band 属性的操作。(图 5.11).
所有的band的特征将在第8章被解释。
所有的元素都有公共的属性在属性窗口“common”的选项卡 (图 5.2).主要关系到元素在页面上的位置:以下对不同属性的描写。
并列和同等规格的总是被确定为72个像素大小。
Band 指元素所属的band。所有的元素只属于一个band,一个band总联系着这个元素。元素的安放总是在band中。
Top 所属band的元素的上边到band顶部的距离
Left 从band左边到元素右上角的距离
Width 指元素的宽度s the element width;
Height 指元素的高;它实际上的值是根据打印时真实值的高度来确定
表格5.3
Foreground 指文本被打印的颜色和元素的线和拐角被画的颜色。
Background 指元素背景填充色
Transparent 这个选项表示元素为透明的,透明的元素的部分应该被背景色填充。
警告:不是所有的输出格式都支持透明的。
Remove Line When Blank This这个选项允许拿走垂直空间占有的对象,如果不太明显,这个元素option allows to take awaythe vertical space
occupied by an object, if this is notvisible; the element visibility is
determined by the value of the expressioncontained in thePrint
When Expression attribute;thinking about the page as a grid where
the elements are put, the line which theelement occupies, is that
where it is put;
表格5.4
表格5.3加亮元素的一条线;为了真正的删除这条线,所有的元素占有仅仅一部分线,有null值的地方 (没有被打印出来);
Print in first whole band 这个选项保证当元素值溢出时被打印在下一页或下一列;这个保证被使用当Print repeated values是不活跃时
Print when detail overflows 这个选项允许打印元素以页或列,如果band不能被打印当前的列或页
Print repeated values this 这个选项决定时候打印元素,当该元素的值与前一条记录值一样时
Position type 在band被改变的情况下确定顶部的坐标。三个可能的值是:
- FixRelativeToTop 预先定义位置类型;这个坐标从来不改变的。
- Float 元素进一步的向底边增加他们的高度,依据前一个元素的高度
- FixRelativeToBottom 元素维持其到底边的距离的常量值;被用做单独记录的分割线。
Print when group changes 所有的报表的组被显示在这个下拉框中;如果他们中的一个被选择,仅当表达式改变组的时候被选择的一个将被打印,此时一个新的坏的组被选择。
Key 它是元素的名字,只有一个意义(iReport自动提出),被程序用来在运行时修改需要修改的文件的属性;
Stretch type 这个属性用来定义详细打印期间元素的高度;可能值有以下几种::
- No stretch 预先改变类型利用元素的高度保持一直。
-RelativeToBandHeight 元素成比例的增加使band增加,用来模仿表格的垂线。
- RelativeToTallestObject 这个选项修改一个元素的高度从而改变离它最近的元素:它也被用做element group,这是一个元素组机制,但不能被iReport管理。
Print when expression itis它是一个java表达式像那些描述在第三章,返回一个boolean对象;除了这些元素之外,这些表达式也被连接到band中。如果表达式返回true,这个元素是隐藏的。一个null值和一个新boolean(true)关联,将被无条件打印出来。
图形元素的画一个对象像一条线或一个长方形;他们通常不显示数据,但是他们却更多的被打印出来,从美学上看他们更有趣,通俗易懂。所有种的元素分享这个“Graphics element”选项卡在这个属性窗口:
图5.11
Pen 指你画线和框架的厚度;它可能的值是:
- None 厚度为null,表示禁止画线或边框
- Thin 线和边框的厚度最薄,但可以看的到。
- 1Point 线和边框被设置为一个像素;
- 2Point 线和边框被设置为两个像素;
- 4Point 线和边框被设置为四个像素;
- Dotted 线和边框被画用虚线;
Fill用来修改填充背景;仅仅被允许使用fill值,完全的填充
在JasperReports中线被定义为长方形,有对角线的长方形。
图5.12 图5.13
这个线被画用Foreground色和Pen厚度.
Line direction it被表示成两条对角线,可能的值是:TopDown (看图 5.13) 和BottomUp.
长方形常用在一个元素周围的边框。这个边界用一个较粗的前景色线来画的。背景被背景色填充,如果这个元素没有被定义为透明的话。
长方形的特殊性在JasperReports中会有一个rounded comers。被定义的意思的是radius属性,描绘弯曲度在你画一个拐角时,使用像素。
图5.14 图5.15
椭圆是唯一一个没有自己的属性的元素。至于长方形,它的边框也是用一个粗的颜色是前景色的线画的。背景被背景色填充如果这个元素没有被定义为transparent。椭圆被画在长方形内切边与长方形的四条边相切。
图5.16
图象是最复杂的图形对象。可以使用raster images
(像GIF, PNG, JPEG,etc...)插入到报表中,但它也能被使用作为一个 canvas
对象,, 你可以画一种箱子:这个图象元素被使用,例如,画一个图表或一个条形码。
图5.17 图5.18
可以使用自己写的类使用net.sf.jasperreports.engine.JRRenderable接口。一个文本连接(“Hyper Link” tab)能够关联到一个图形元素。我们将分析文本连接在这章。
图象特征可以在“Image”选项卡设置.
Image Expression 它是一个java表达式。它的结果是一个被Image
Expression Class属性定义的对象;
Expression Classattribute;以返回类型为理由,图象被装载改变的方式;
Image Expression Class 表达式的返回类型。
表格5.4概述了Image Expression Class可能采用和描述Image Expression结果的解释。
|
|
java.lang.String |
一个被认为是一个文件名的字符串;JasperReports将设法解释这个字符串想一个绝对的路径;如果没发现文件,它将设法加载指定名字的源从classpath 正确的表达式是: “c:\\devel\\ireport\\myImage.jpg” “it/businesslogic/ireport/icons/logo.gif” |
java.io.File |
指定一个文件对象作为图象去加载。 正确的表达式可能是: new java.io.File(“c:\\myImage.jpg”) |
java.net.URL |
指定It specifies the java.net.URL 对象。当你以html格式输出报表时使用它。 正确的表达式可能是: New java.net.URL(“http://127.0.0.1/test.jpg”) |
java.io.InputStream |
指定一个准备读取的java.io.InputStream对象;既然这样我们就不能考虑图象存在或它在一个文件中:大多数我们读取一图象从数据库,返回一个inputStream。 正确的表达式可能是: MyUtil.getInputStream(${MyField}) |
java.awt.Image |
指定一个java.awt.Image对象;当一个图象对象被创建时大概返回一个简单的对象; 正确的表达式可能是: MyUtil.createChart() |
JRRenderable |
指定一个使用 net.sf.jasperreports.engine.JRRenderable接口的对象。 |
表格5.5
在这个例子中我们有解释, MyUtil 代表一个创造类。如果你想使用一个创造类通过调用一个static 方法去运行特殊的细节,就需要使用一个属于它的包(例如
it.businesslogic.ireport.util.MyUtil)或者用import(菜单Show→Importdirectives in the report)引用这个包。我们解释这个方法在这个例子中是static的,但当我们讨论变量时,我们将看到如何实例化一个类在打印开始时和如何使用它在表达式中。
在Image Expression 中使用字段,变量和参数,它将加载或生成图象以变化的方式(像已经发生的,例如,在detail中生成一个有图象的目录,每个目录关联一个图象)
Scale Image 定义图象怎样适应元素的尺度;可能的值有三个:
- Clip 图象的尺寸不改变;
- FillFrame 图象适合元素的尺寸(变的不成型)
- RetainShape 图象适合元素的尺寸,但保持图象原来的比例
图5.19 图5.20 图5.21
Using cache this如果这个元素最近将被再打印,可以使用这个选项使图象保存在内存中以便下次使用;图象只有被Image Expression Class 设置java.lang.String 才能使用cache;
Vertical alignment 这个属性用来定义图象为垂直队列依照元素区域; 可能的值是:
- Top 图象排列在顶部;
- Middle 图象被放在垂直队列的中间在元素区域中。
- Bottom 图象被排列在底部;
Horizontal alignment 依照元素区域,图象被定义到水平队列;可能的值是:
- Left 图象排列在左边;
- Center 依照元素区域,图象排列在水平中间
- Right 图象排列在右边;
Evaluation time 定义Image Expression被处理时的状态;事实上表达式的赋值能被做当报表引擎在创建报表时遭遇这个元素或或者也能延缓在一些特殊的案例中:例如,你想去计算一个子报表。
赋值时间是一个非常有用的功能,它将被解释在JasperReport手册中。可能的值:
- Now立即求表达式的值;
- Report 报表产生之后求表达式的值;
- Page页未求表达式的值 ;
- Column列末求表达式的值;
- Group求每组的值;
Evaluation group 同Evaluation time.
图形元素(和text元素)可以被想象成一个框架,或者可以定义它的四个边的值。
它是元素元素和内容之间的空间。边框和内容被定义在border选项卡。他们的属性被介绍在JasperReports0.6.3版本。
图5.22
可以选择元素的类型和画每条边的颜色。边框类型是:
- None,厚度为null,允许不打印出边框线;
- Thin最小厚度;
- 1Point 一个像素的厚度;
- 2Point 2个像素的厚度;
- 4Point 4个像素的厚度;
- Dotted最小厚度的虚线;
一个简单查看图形效果的方式就选择border选项卡中间的显现边框。
和图形元素一样,文本元素也有一个允许查看的公共属性:它是font选项卡(图5.24)
图5.23
“font” 选项卡是用来设置元素中文本的属性,所以不仅仅是字体(字符的类型和尺寸),还有文本的队列,可能的rotation和线空间。
Report font 是一个边框形式字体的名字。从这里可以设置所有字符的属性;边框形式字体可以定义报表的标准和管理他们通过选择菜单:View→Report fonts;
Line spacing行间距,可能的值是:
- Single 单行写如;(预先确定值predefined value);
- 1_1_2 间隔一行半写入;
- Double 双倍写入;
Horizontal align依照元素使文本水平排列;
Vertical align依照元素使文本垂直排列;
Rotation 允许指定文本如何去打印,可能的值是:
- None 文本将正常的打印从左到右,从上到下;
- Left the 文本逆时针旋转90度,它被打印从底到顶,水平和垂直队列依照文本;始针4
ionports0.6.3banbe the 旋转(例如,文本开始打印将沿这底边垂直队列画顶的界限开始);
- Right the 文本顺时针旋转90度从顶到底,垂直和水平队列依照文本旋转;
图5.24
其他的字符的字体设置将在第六章详细解释。
文本元素的属性可以用文本工具(图5.25)条来进行修改。
图5.25
static text被用来展示非动态文本的报表。 仅仅一个参数用来区别这个元素从一个普通文本元素在static text选项卡中,指定你要看到的文本:它是一个标准的字符串,不是表达式,也不需要依照java语言的规范。
图5.26 图5.27
文本区域可以打印一个java表达式的值。简单例子就是打印一个字符串(java.lang.String)来自于一个表达式想这个:“Hello World!”
既然这样,静态文本创建就需要很小的工作量,因为这个例子中字符串是一个不变的值;其实用java表达式去定义文本区域允许包含非常高的控制超长文本。不直接的联系文本元素(像发生在不同报表工具文本元素隐含的数据库字段的值):不同字段的记录值,是可利用的通过datasource,存储在一个名字叫fields的对象中,他们被访问依靠表达式的语法在第三章。
和图象一样,一个超文本连接可以关联textfields,可以在HyperLink中定义
图5.28 图5.29
TextfieldExpression Class it指定域表达式的返回类型;可能的值有许多,包括所有的由sql类型组成和一些管理数据的类的java对象,以下表格列出了所有可选的类型的。
|
java.lang.Object |
java.lang.Boolean |
java.lang.Byte |
java.util.Date |
java.sql.Timestamp |
java.sql.Time |
java.lang.Double |
java.lang.Float |
java.lang.Integer |
java.io.InputStream |
java.lang.Long |
java.lang.Short |
java.math.BigDecimal |
java.lang.String |
表格5.6
列表中包含的 java.lang.Object 类型能够在没有其他类型可以联合数据使用。
Evaluation time 指定报表创建Textfield在那个阶段赋值;
Evluation group 是否赋值在一个组后
Stretch with overflow当它被选择时,这个选项可以垂直的改变内容,如果元素不能充分的包含在文本线内的话;
Blank when null当域表达式返回一个null值时允许打印空白;
Pattern 指定字符串可以使用Format类,指定用Textfield Expression Class是正确的。
JasperReports能格式化日期和数字;下面的表格概述中的一些例子包括字母和他们的意义可以快速的创建日期和数字。
|
|
|
G |
Era designator |
AD |
y |
Year |
1996; 96 |
M |
Month in year |
July; Jul; 07 |
w |
Week in year |
27 |
W |
Week in month |
2 |
D |
Day in year |
189 |
d |
Day in month |
10 |
F |
Day of week in month |
2 |
E |
Day in week |
Tuesday; Tue |
a |
Am/pm marker |
PM |
H |
Hour in day (0-23) |
0 |
k |
Hour in day (1-24) |
24 |
K |
Hour in am/pm (0-11) |
0 |
h |
Hour in am/pm (1-12) |
12 |
m |
Minute in hour |
30 |
s |
Second in minute |
55 |
S |
Millisecond |
978 |
z |
Time zone |
Pacific Standard Time; PST; GMT-08:00 |
Z |
Time zone |
-0800 |
表格5.7
这儿是一些dates和timestamp的格式例子:
|
|
"yyyy.MM.dd G 'at' HH:mm:ss z" |
2001.07.04 AD at 12:08:56 PDT |
"EEE, MMM d, ''yy" |
Wed, Jul 4, '01 |
"h:mm a" |
12:08 PM |
"hh 'o''clock' a, zzzz" |
12 o'clock PM, Pacific Daylight Time |
"K:mm a, z" |
0:08 PM, PDT |
"yyyyy.MMMMM.dd GGG hh:mm aaa" |
02001.July.04 AD 12:08 PM |
"EEE, d MMM yyyy HH:mm:ss Z" |
Wed, 4 Jul 2001 12:08:56 -0700 |
"yyMMddHHmmssZ" |
010704120856-0700 |
表格:5.8
|
|
|
|
0 |
Number |
Yes |
阿拉伯数字 |
# |
Number |
Yes |
阿拉伯数字, 0作为缺省的 |
. |
Number |
Yes |
十进制或货币分割付 |
- |
Number |
Yes |
负号 |
, |
Number |
Yes |
分组号 |
E |
Number |
Yes |
科学符号中为单独是尾数和说明Separates mantissa and exponent in scientific notation.不需要引用像素或后缀Need not be quoted in prefix or suffix. |
; |
Subpattern boundary |
Yes |
分割正负样式Separates positive and negative subpatterns |
% |
Prefix or suffix |
Yes |
乘以100使用百分号 |
\u2030 |
Prefix or suffix |
Yes |
乘以1000使用每一千 |
¤ (\u00A4) |
Prefix or suffix |
No |
Currency sign, replaced by currency symbol. If doubled, replaced by international currency symbol. If present in a pattern, the monetary decimal separator is used instead of the decimal separator.
|
' |
Prefix or suffix |
No |
引用特殊字符Used to quote special characters in a prefix or suffix, for example, "'#'#" formats 123 to "#123". To create a single quote itself, use two in a row: "# o''clock". |
表格5.9
这是一些数字格式的例子:
|
|
"#,##0.00" |
1.234,56 |
"#,##0.00;(#,##0.00)" |
1.234,56 (-1.234.56) |
表格5.8
图5.30
通过“create”按钮可以打开样式编辑,意思就是数字,日期,流通等的简单格式
Textfield Expression 是一个打印表达式,返回在Textfield Expression Class;中设置的类型
可以转变StaticText在Textfield通过选择和按F3键。
为了能快速的改变一个或更多个普通文本元素的表达式可以选择他们和按F2键。
子报表是包含在其他报表中的一个元素,由一个jasper文件和在子报表的属性中被指定的datasource提供数据开始创建。
以下是关于子报表特征的一些简要插图。由于这个子栏目的复杂性,我们将在另一章详细解释。
图5.31
图5.32 图5.33
Parameters Map Expression定义一个运行时赋值的表达式;这个表达式必须返回java.util.Map;这个map中包含一些名值对,这些名值对将被传递给子报表作为参数值;
Connection/Datasource expression定义一个返回JDBC连接或JRDataSource用来填充子报表;
Using cache保存或不保存数据到内存,以便能快速的加载数据再下一次;
Subreport expression 定义一个在运行时返回Subreport expression class对象的表达式。依照返回类型,表达式被赋值为了恢复用来生成子报表的jasper对象;
Subreport parameters this这个表可以定义一些使用适当的表达式动态提供的名值对给子报表;
除了JasperReports提供的原始的元素外, iReport提供两个”合成”元素为他们的普通属性和补偿使用图形元素。
所有关于图表的信息查看14章。
这个元素可以创建一个动态的条形码形式的值。
图5.34 图5.35
指定打印条形码的类型。
可能的值列表在表格5.9
|
2of7 |
3of9 |
Bookland |
Codabar |
Code128 |
Code128A |
Code128B |
Code128C |
Code39 |
EAN128 |
EAN13 |
GlobalTradeItemNumber |
Int2of5 |
Int2of5 |
Monarch |
NW7 |
PDF417 |
SCC14ShippingCode |
ShipmentIdentificationNumber |
SSCC18 |
Std2of5 |
Std2of5 |
UCC128 |
UPCA |
USD3 |
USD4 |
USPS |
表格5.9
Checksum itspecifies the code控制条形码是否被打印指定的代码;这个特征仅仅被条形码的一些类型提供;
Show text指定是否打印文本 ;
Barcode Expression 指定以来于条形码的表达式的值;总是返回string类型的值;
Evaluation time eEvaluationgroup 和图形元素的意思一样;
图形和文本域元素都能被用做一个anchor(锚)在文档中,作为超连接指向源或其他本地的anchor(锚)。锚是一种特殊的“标签”用来识别文档中特殊的位置。
超连接和锚可以通过Hyper Link(图5.35)选项卡来定义。这是分开的两部分。上部分是一个文本域,这里可以指定这个锚的名字。这个名字可以被其他的连接引用。
下部分是定义连接的源或文档中的位置。
JasperReports 可以创建五个类型的超连接: Reference,
LocalAnchor, LocalPage, RemoteAnchor andRemotePage.通过Hyperlink target it 可以指定打开一个连接在当前的窗口(预先设定,目标是self(本身))或一个新的窗口(Blanktarget)。
警告:不是所有的输出格式支持超连接。
图5.37
The Reference 连接指出一个外部的源,一个可以被通过点URL识别的源,例如一个服务器去管理向下的记录功能。这表达式仅仅需要HypelinkReference Expression.
点这个本地锚意思就是创建一个连接在同一文档之间。它可以被使用,例如通过一个标题的连接可以找到相应的章节。
定义一个本地锚去要指定一个能生成有效名字的Hyperlink AnchorExpression
如果指示代替锚,你想点一个详细的当前报表页,就需要创建一个LocalPage连接。既然这样就需要指定这个指示页的号码,意味着使用HyperLink Page Expression(这个表达式返回一个Integer对象)。
如果你想点一个外部的锚的细节,你就要使用RemoteAnchor 连接。既然这样你就要指定在Hyperlink Reference Expression域和指定HyperlinkAnchor Expression来命名锚
这个连接可以点进一个文档外部的详细页面。既然这样,这个外部文件的URL点就指定Hyperlink Reference Expression域和这页的号码将指定HyperLink Page Expression。
Fonts是描述文本的特征 (形状和尺寸)。在JasperReports中可能指定每个元素的字体属性。而且它可以定义一个名叫“report font” 的球形字,并联合他们到文本元素。这个球形字体将被包含在元素中使用。
通常一个字体被定义有四个特征:
- 字体名字(字体家族)(font family)
- 字体尺寸
- 属性(黑体,斜体,带下划线,barred)如果以PDF输出报表,JasperReports需要一些附加的信息:
PDF Font Name 这是预先确定的PDF文档字体的名字;
PDF Embedded 这是指定一个字体的标记,像“external TTF”类型,
应该被包含或不包含在PDF文件。.
PDF Encoding 这是一个字符串,用来指定字符编码的名字。
如果不输出PDF格式的报表,字体使用指定的font name,强化的部分可以指定它的属性。在这种情况下的PDF文档,识别使用的字体和它的属性(黑体,斜体,等)。
其他属性是可以被忽视的,因为他们已经被包含在PDF字体中。
可能要使用扩展的TTF(TrueType font)。为了能够使用,就必须将扩展字体(扩展名为.ttf的文件)加到CLASSPATH中:这是需要的在设计(使用iReport时)和生成(通过一个java程序,swing或servlet生成报表时)时。
警告:需要避免添加数以百计TTF到CLASSPATH中,因为可能这可能使iReport启动向下运行变的缓慢。你可以添加一个%WINDIR%\fonts目录在CLASSPATH中。
图6.1
In the 在“Font name”选择框中仅仅是系统字体,通过java虚拟机来管理,通过操作系统来展示他们。意思就是说如果你想使用扩展的TTF字体在非PDF报表中,你就需要去安装它到系统中。
安装后,在PDF Font Name选择框中选择 “External TTF Font...”。
选择框中的“True Type font”将被激活。它将在classpath中查找所有的True Type fonts引用它。
为了能在输出的PDF中正确的显示字体,需要选择“PDF Embedded”。
字符编码是JasperReports中最模糊的问题之一;这个问题可能发生在打印PDF格式的报表时。所以选择一个正确编码格式是非常重要的。
编码如何指定的说明:The encoding specifies how thecharacters have to be interpreted:在意大利,例如,如果你想打印合适的重音(像è, ò, a, ù,等),就需要使用编码(西方,欧洲一个ANSI标准Western European ANSI aka WinAnsi)。然而一些JVM版本不支持编码(1.4版本以前的),但他们可以使用编码(唯一不同的是小写的”p”)。
iReport 富含预先设置的编码类型在选择框去选择。
如果你在一些不标准的PDF格式报表中有一些问题,确认所有的字段的编码类型是否一致,核实从数据库读取的报表的数据的编码设置。
可以使用Unicode写不标准的字符(像希腊,西里尔和亚洲字符);Unicode编码可以被指定在表达式中,用来识别这些文本。例如,如果你想打印 euro符号,可以使用Unicode \u20ac字符来避免乱码。
注意:表达式\u20ac不是简单字符串,它是识别包含?字符的java表达式。如果写一个静态文本到文本元素中,\u20ac就能打印出来,因为它是静态文本,所以不被作为java表达式类解释(仅仅适合在textfield中)。
可以选择菜单”View”中”Reportfonts” 定义球形字体。会打开一个管理球形字体的窗口。一个新的图解可以查看图6.1。
从”Report font”选择框中选择使用球形字体在文本域中(图5.23)。
iReport不做管理使用最理想方式的“report font”。限制自身选择报表字体的属性值到文本元素中和复制所有的球形字体到文本元素中。事实上,球形字体天生就是为节约XML源为目标的,来避免为每一个单独的域指定字体。因为这些工作都可以被iReport自动的完成,最优化的重要性并不大。然而,因为这个企图集中报表字体的管理被迫停滞,因为,如果提出,JasperReports用属性来指定元素的标准,他们总是被指定被。 更简单的,如果一个球形字体报表被使用在文本元素,它的特征被保存在文本元素,本质上还是球形字体报表。
显然一会儿工夫不可能设置超过一个报表的字体。
报表中有三个组的对象可以存储值:字段,参数和变量。这些对象被使用在表达式中,他们能改变他们的在打印行期间值和他们的类型,所有他们都有一个符合java类的类型,像string或double。字段,参数和变量被提出在报表中。选择主菜单”view”,我们能够发现“Report fields” (这就是字段), “Reportvariables” and “ReportParameters”这些子菜单.这三个子菜单中的每一个都允许我们管理所有他们的对象(图7.1)
图7.1
通过这个窗口我们可以声明,修改和删除字段,变量和参数。
一个打印一般从数据源开始被创建:数据源在JasperReports 总是被组织成一组记录,这些记录被排列通过一系列正确字段像一个sql查询的结果集。
在图7.1的窗口中,”Fields”选项显示了需要打印的字段。为了能够显示这些字段,可以按”new”按钮,从而打开一个新的窗口(图7.2)
图7.2
字段被定义通过名字,类型和一个特需的描述。你不可能任意选择一个字段的类型。如果这个字段类型不在预选的类型中,就可能断言这个字段作为java.lang.Object和投射他到一个必须的类型在字段被使用的表达式地方。在表达式中你可以查看一个字段通过使用语法:
所以你可以处理MyPerson字段作为it.businesslogic.Person对象:
((it.businesslogic.Person)$F{MyPerson})
这些字段在报表中不可能知道它的高度(也就是延伸到达数百)。为这个原因,iReport可以使用不同的工具通过精确的数据源型声明字段。
这个最广泛使用工具可以断言或记录一个sql查询的字段。首先需要做的是打开ReportQueryDialog(按按钮).
查询解说的窗口将打开。它将用来填充报表(如果这个报表的创建来自于一个sql查询)
图7.3
之后要使DBMS连接起作用,就要插入一条查询语句(跳到15页查看如何做),例如:
select * from customers
iReport将运行这个sql查询,分析结果以便你去选择想要显示的字段名称。通过选择字段,他们将被注册值(图7.4)。
图7.4
注意:注册字段,就需要选择他们从这个列表中;如果查询包含一些错误,取消“AutomaticallyRetrive Fields”选择框,并按“Read fields”按钮读字段。
既然这样所有的字段将被变为String类型:一般的类型设置基于最初的sql类型,能够被String ,Integer等匹配。
JasperReports中最高级的特征之一是管理datasources的可能性,不基于简单的sql查询。这是一个javabean收集的事例,记录集的概念被java对象替代。在这个用例中”fields”是对象的属性。通过选择JavaBean Datasource选项,可以注册被读取的字段,这些字段被java对象指定,这个对象是:
it.businesslogic.ireport.examples.beans.AddressBean
图7.5
按钮之后可以指定这个类的名字,这个属性被萃取和引用作为字段。
从javabean收集中创建一个打印,就需要用一个名叫JRBeanCollectionDataSource的datasources(JasperReports装载的)。
iReport提供一个扩展的datasource版本,可以用这个方法来重新声明断言作为一个字段值:
person.getAddress().getStreet()
我们讨论了JRExtendedBeanDataSource。之后我们将了解一下datasource精确的方法,在“JavaBean Ext Datasource”选项卡有一个类似的工具查看JavaBean的反省(introspectionof a JavaBean)。这个工具不能返回一个清晰的列表来探究javabean,但可以通过一个树型结构可以更深的探究,简单属性(想strings和numbers)可以通过树叶显示,其他更多复杂的类型(其他bean)可以进一步的了解到。
这种方式可以到达bean的细节,在类似的java包中。
图7.6
为了添加一个字段到列表中,就需要选择树的接点和按“Add field(s)”按钮
在文本元素中打印一个字段,需要设置正确表达式和textfield类型,如果需要的话,还可以为字段的格式定义恰当的模式。
可以自动的设置表达式通过从字段列表中拖拽字段到相应的band (drag ‘n’ drop feature).
参数是程序用来传递给报表的一个值,创建打印时,他们能被用指导运行阶段行为和(作为应用程序在sql查询中的条件),提供额外的数据去打印,像一个图形对象包含一个字符或字符串在报表标题中。
和字段一样,参数也有类型,要在设计时提出来。这是参数类型能被选择的。
图7.8
此时这个属性“is for prompting”不被ireport使用,它和打印是无关的:它代表是一种传入机制,jasperReports允许应用程序识别用户立即使用的参数值。
我们在表达式中提及到的参数的语法:
如果应用程序不直接传给报表一个参数值的话,它可以设置一个默认的值。想想由于参数是一个java对象,所以它不可能用表达式像这样的
0.123 (Wrong expression)
一个double类型的参数值,但是可以先声明一个double对象像这样:
new Double(0.123) (Right expression)
我们有说过,可以用参数在sql查询中。假如你想打印一些信息,关于通过客户ID(在设计时并不知道)得到详细信息。我们的查询就要变一下:
select * from customers
where CUSTOMERID =
哪儿的MyCustomerId是一个Integer类型的参数。JasperReports 将传入这个查询:
select * from customers where CUSTOMERID =
它将运行这个sql用一个预编译语句通过传递MyCustomerId的值作为查询参数。如果你想避免这种参数管理,可以用特殊的语法:
that 它允许我们在查询中用它的值取代它的名字。例如我们有一个名叫MyWhere的参数,它的值是“where CUSTOMERID = 5”,一个查询像这样:
select * from customers
在完成期间被传入:
select * from customers where CUSTOMERID =5
参数被程序指令传递给打印机通过一个类,它继承java.util.Map接口。
考虑到的代码列表在27页,以下是一部分:
...
HashMap hm = new HashMap();
...
JasperPrint print =JasperFillManager.fillReport(
fileName,
hm,
new JREmptyDataSource());
...
fillReport表示所有JasperReports库中的一个键的方法,它可以创建以一个jasper文件名为参数的打印,一个datasource (一个假的datasource, JREmptyDataSource) 和一个参数map,它在这个例子中是空的,用java.util.HashMap对象表示的参数。
用这个参数包含在报表的标题中 (图 7.10 and 7.11), 我们继续这种方式:
1. 首先声明这个名为NAME _REPORT,类型为java.lang.String的参数;
图7.9
2. 添加一个textfield元素的标题band用这个表达式:$P{NAME _REPORT}
3. 修改程序代码:
HashMap hm = new HashMap();
...
JasperPrint print =JasperFillManager.fillReport(
fileName,
hm,
new JREmptyDataSource());
我们放这个参数map中的一个值为NAME_REPORT参数。.
图7.10 图7.11
不强制通过程序传递一个值为所有的参数在报表中声明。如果程序不明确设置一个参数值,JasperReports将用Default Value Expression考虑预先设置的参数值,empty表达式和null的意思一样。事实上,这个例子并不是很有意思的,因为这些数据类型是一些简单的string,然而可能也会提供给报表通过map,一些更复杂的对象,像图象(java.awt.Image)或者一个可用的datasource提供一个详细子报表的情况。此外,提供一个和声明的参数一样类型的对象也是很重要的,否则将会抛出一个ClassCastException异常
JasperReports提供了一些内置的参数(报表引擎内在的),这些参数是易读的,但是不可能被用户修改。这些参数(表7.1)。
|
|
REPORT_PARAMETERS_MAP |
它是java.util.Map传递给fillReport的方法,其中包含了用户定义的参数值。 |
REPORT_CONNECTION |
它是通过sql查询创建报表时传递给报表的JDBC连接。 |
REPORT_DATASOURCE |
它是没有使用JDBC连接时创建报表时用的datasource。 |
REPORT_SCRIPTLET |
It 指创建报表时用到的脚本实例,如果没有指定脚本,参数就聚焦到net.sf.jasperreports.engine. JRDefaultScriptlet实例上。 |
表格7.1
变量是一个用来存放计算结果的对象,就像小计,总计等。
图7.12
和字段参数一样,变量被打出来,必须声明一个他们要求的java类型(Variable Class Type)。
图7.12显示了如何创建一个新的变量,看一下每个字段的意思。
Variable name他是变量的名字;和字段,参数一样,我们提供下面这个表达式
Variable Class Type 和参数一样,不用考虑对象的类型;在选择框中包含了大多常用的类型像java.lang.String andjava.lang.Double;
Calculation Type可以预先设置的变量的类型是:
“Nothing” 不用执行任何计算JasperReports 执行计算来自于datasource的每条新的记录: 执行计算变量的意思就是求它的表达式(VariableExpression);如果计算类型是noting,JasperReports就将变量的表达式赋给变量值;如果计算类型不是noting,表达式的结果将计算一个新值赋给变量。计算类型有:
|
|
Nothing |
不是计算类型,它被使用当用户指定内在的表达式时,每条记录将被赋值。 |
Count |
计算所有非null的表达式的次数,不要把它和sum搞混了,sum是计算的是数字的结果 |
Sum |
反复的迭加表达式当前的值 |
Average |
计算所有标准输入的表达式的平均值。 |
Lowest |
返回标准输入中最低的表达式的值 |
Highest |
返回标准输入中最高的表达式的值 |
StandardDeviation |
返回标准输入中规格不同的表达式值standard deviation |
Variance |
返回标准输入中不一致的表达式值 |
System |
不是一种计算方式,也不求表达式的值;使用这个后,报表引擎仅将最后一个变量值放入内存中;它可以用脚本来存储计算结果。. |
表格7.2
ResetType 当变量值被重设Initial Value为或简单null的时需要指定;变量重设是基本的,当你想求一些组的小计或平均值时;重设类型列表(表7.2):
|
|
None |
这个Initial Value Expression总是被忽视 |
Report |
这个变量被初始化一次仅仅在报表创建的开始,用Initial Value Expression。 |
Page |
这个变量被初始化在每个新页 |
Column |
The这个变量被初始化在每个新列(或者在每页,如果这个报表页仅仅只有一列的话) |
Group |
The 这个变量被初始化在每一个新的组(我们定义这个组用Reset Group) |
Reset Group 当组选择Groupreset类型时用来指定变量的重置
Custom Incrementer Factory Class一个叫JRIncrementerFactory接口的java扩展类用来定义像总计一个非数值类型的操作;
Variable Expression 用来识别输入值的java表达式
Initial Value Expression用来给变量初始化一个值的表达式
像参数, JasperReports安置了一些用户处置的内部变量,他们是易读的,但不能被用户修改。这些变量是:(表格7.4)
|
|
PAGE_NUMBER |
包含了当前页码,在整个报表中包含总的页数。 |
COLUMN_NUMBER |
包含当前是第几列 |
REPORT_COUNT |
指示当前正在处理的是第几条记录 |
PAGE_COUNT |
当前页中包含了几条记录 |
COLUMN_COUNT |
当前列中包含了几条记录 |
|
以变量前缀来表示这个组记录数 |
表格7.4
在这章我们将介绍如何用iReport管理band和group。第四章我们已经介绍了报表的结构,看到了报表是什么样子的。这里我们将用到组,如何创建一个breaks,如何管理子报表等等。
JasperReports将报表划分成7个主要的band和一个背景(总共就是8个bands)。其他两个(the group footer 和 the group header)bands被加进去。
按按钮进入bands列表(图8.1)。
图8.1
通过这窗口可以修改band的主要的属性,它的高,用像素表达(band高),可能band就会被打乱,如果超出了这页(Split allowed)和Print When Expression,这个表达式将返回一个boolean型的对象,并终止打印。就是这个空的表达式隐含了这个表达式从而使band总是被打印。键入表达式可能是字段,变量和参数,通过指令生成一个boolean对象的结果。
甚至指定一个合法的值,band的高也可能增大,如果一个或多个元素垂直的增加(可能在textfield元素内容或子报表超出了指定的尺寸的情况下发生).的话。 JasperReports可以保证band的高永远不会这样。
In 为了能调整band可能要用到bands窗口(图8.1),立即设置一个值在band的高的地方,或者用鼠标移动指针到次的空白处,拖拽它到页的底或顶。(图8.2)
双击次的空白页可以调整band通过改变内容的高度。
图8.2
如果一个或更多个bands没有高度,可以按shift键将它朝底边的空白移动来增加它的尺寸。
移动一个元素从一个band到另一个,这个元素就会自动被这个band关联起来。
分组可以将报表的记录分开成组。通过一个表达式可以定义一个组。JasperReports求这个表达式的值:一个新的组开始当这个表达式的值改变时。
我们将解释组的用途,通过一步一步的举例。
假设有一个人员列表:我们想创建一个以人名分组的报表,人名按首写字母分组(就像一个电话簿)。运行iReport打开一个空白报表页。用sql查询从数据库中取数据(一个JDBC连接到Northwind数据库,而且这个数据库已经配置完成并测试通过)。按照你记录做第一件你认为的选择:The first thing you have to think to is the order of record
selection: JasperReports获得没有顺序的记录从datasource;为这个原因你认为如何打印这些内容?你得自己把记录排序以正确的方式。我们用这个sql查询:
select * from customers order by CONTACTNAME
通过这种方式就可以将你选择的客户按他的名字排列。选择CONTACTNAME和COUNTRY字段通过Report Query Dialog (图8.3)。
图8.3
创建组之前,务必正确完成每件事,将CONTACTNAME和COUNTRY字段插入到details:编译和创建报表。结果应该和图8.5类似。
图8.4
加点难度,我们可以将detail分成两列(查看33页)。
图8.5
继续数据分组:通过 “groups”按钮进入报表分组管理窗口。
图8.6
按New按钮插入一个名字叫“Initial”的组
图8.7
可以通过以下几个属性定义组:
Group Name 定义组的名字,这个名字将被用来命名和这个组关联的两个band:header和footer;
Start on a new column 如果这选项被选择,它允许强制在组的最后将列分开(意思就是新开一个组);如果这个报表仅有一列,这列就将被分开;
Start on a new page 如果选择这项,它允许强制在组的最后将页分开(意思就是新开一个页);
Reset page number 这个选项可以重设页码在一个新组开始时;
Print header on each page 如果选择这项,就可以在所有的页打印组头的内容(如果一个组的内容比一页内容还多时,在新页上也打印组头);
Min height to start new page 如果不是0的话,JasperReports 将在每页打印组,如果有效的空间被指定为次的空白;通常这是为了避免分割(就像一整段被分开一样);
Group Expression 是JasperReports计算记录的表达式;当表达式换一个值时,一个新的组就创建,如果表达式是空的话,既然这样,这个结果就是单一的组的头和单一组的角分在第一列头之后和最后一列角之前;
Group Header Band Height it is the band height representing the groupheader:
像band一样,它可以在bands窗口(图8.1)中被修改;
Group Footer Band Height it is the band height representing thegroup footer:它也可以被修改想bands窗口一样(图8.1);
In在我们的例子中我们命名为“Initial”(名字的首写字母)的组,我们将告诉它开始一个新的列(选择Starton a new pageoption)。我们的表达式返回这个名字的第一个字母;这个表达式是:
$F{CONTACTNAME}.substring(0,1)
并不能充分的做我们想做的事,因为没人敢保证CONTACTNAME字段是否是空值,事实上在赋值时表达式会首先抛出NullPointerException异常,其次是ArrayOutOfBoundException异常。我们测试这个内容用这两个表达式:
( ($F{CONTACTNAME} != null &&$F{CONTACTNAME}.length() > 0) ?
$F{CONTACTNAME}.substring(0,1) : "")
我们用java约束它:
( condition ) ? value1 : value2
如果条件是true就返回value1,相反就返回value2。
具体的说就是如果字段是null或它的长度为0,返回一个空的字符串,否则就返回名字的第一个字母。
添加一个新组,在下面图上有两个新的bands: InitialHeader
and InitialFooter。 用表达式在textfield插入InitialHeader band 作为组(图8.8)。结果在图8.9。
图8.8
图8.9
报表中组的数量可能是任意不确定的,一个组可能被一个父组包含,父组包含其他的子组。结果就是一个组列表。
可以修改一个组与另一个组的关系的优先权,通过点组列表框架上的“MoveUp”和“Move Down”按钮(图8.6)。
改变组的优先权的意思就改变这这个组的位置。优先权越高,组bands就越远离detail。
子报表是JasperReports目前最强大的功能之一,它可以实现非常复杂打印。它的目的就是将一些形态相似的报表插入到另一个报表中。
创建这样的打印需要三件事做:一jasper文件,一个参数map(可以是空),一个datasource(或一个JDBC连接)。这章我们将介绍如何利用这三个对象来创建一个动态连接的子报表,在父报表数据的基础上来创建。然后,我们将介绍一个小窍门,如何将父报表中的数据提供给子报表。
我们已经说过了,子报表是一个真正的报表,它有自己的XML源,有单独的jasper文件。创建一个子报表就意味着创建一个单独的报表。你得注意打印空白要设置为0为子报表。水平尺寸应该和报表一样大,不需要使子报表元素和子报表一样大,然而为了避免不可预料的结果,最好是精确点。
可以用子报表按钮将一个子报表插入到父报表中;将创建一个类似长方形的元素。子报表元素的尺寸并不意味着真的就这个尺寸,因为子报表将占用所有的没有开始裁切和裁切不正确的空间。我们可能认为只要将子报表尺寸定义到报表左上角就可以整齐的排列子报表了。
将一个子报表连接到父报表也需要确定三件事:如何获得jasper对象来执行子报表,如何将数据传递给子报表,如何给子报表参数设置值。所有的这些信息可以通过子报表属性来确定:(图9.1,9.2)
图9.1 图9.2
这些属性被定义在这两个选项中:Subreport 和Subreport (Other).
正像打印需要从程序中调用fillReport,方法,参数map被传递在子报表创建时;事实上这个map被直接的管理通过报表引擎,但是你有可能去插入名值对在这个map运行时。可能是最少被用来设置子报表参数值的方法,但是子报表元素遇到的第一个属性在属性窗口;它允许你定义一个结果java.util.Map为对象的表达式。可以用这个方法,例如,可以传递给报表控制(来自于程序)一个包含map的参数,然后用这个参数名字的表达式(例如:$P{myMap})传递map给子报表。也可能传递一个相同的参数给子报表,如果这个参数是内置参数REPORT_PARAMETERS_MAP:就可以写这样的表达式$P{REPORT_PARAMETERS_MAP}。如果你离开bland区域,一个空的map就会传递给子报表(这是不正确的如果我们定义我们期望看到的参数值)。这种机制的限制就是所传递的参数的不变性。为了能绕过这个局限性, JasperReports允许你定义一些能被对象单独创建的参数的名值对。你可以查看表在图9.2。你也完全可以自己动手:通过Add按钮可以添加一个新的参数用来提供子报表的参数map。
图9.3
这个参数名和当初在子报表中显示的一样。意思就是“事物敏感性”,意思就是说大小字母是不同的。
如果你写了错误类型的名字,或者你插入了一个不是你定义的参数,没有错误抛出(但是可能会问你一些为什么停止工作的事情)。
这个表达式是JasperReports一流的表达式,他可以用在字段,参数和变量。它返回一个符合子报表中设置的类型的参数,除非ClassCastException异常。
一个或多个“subreport parameters”传递记录值在父报表中打印在父报表中执行子报表的查询用你提取的记录(报表headers 和 lines).
设置字报表的datasource就是为了告诉JasperReports如何获取数据来提供给字报表。这儿提供了两个大的datasource组: JDBC connections和Datasource。
用JDBC来提供报表数据组够了,而且简单。既然这样ConnectionExpression定义一个已经连接到数据库上的java.sql.Connection对象。它可以传递已经打开连接通过明确的参数但是更简单的方法是用预先确定REPORT_CONNECTION参数包含这个连接传递给应用程序的方法。通过使用Connection Expression,
到这为止就可以用sql查询创建一个包含字报表的报表。
用datasource的话就比较复杂:事实上它没有其他概念可以比的上JDBC连接。既然这样就可以就可以通过传递datasource提供给字报表一个参数,这个内部参数REPORT_DATASOURCE就没用了,因为它不可能去提供给子报表。
然而用自主的一种表达式可以生成datasource。
datasource是一种普通“耗材”对象,只能用来填充一次报表;所以datasource作为参数传递只能满足一个子报表。因此这个参数技巧就不适合了,当报表控制得到detail自己的子报表时 (除非控制器只有一条记录)。在我们介绍Datasource时就能发现用习惯的datasources很容易解决这些问题。
指定“.jasper”文件用来创建子报表,我们必须设置Subreport Expression. 这种类型的对象返回先于从选择框中选择的表达式(看图9.2)。可能类型和他们的语意查看表格9.1的列表。
|
|
net.sf.jasperreports.engine.JasperReport |
表示一个在JasperReport对象中预加的jasper文件。 |
java.io.InputStream |
Ijasper文件的打开流 |
java.net.URL |
定义jasper文件的URL文件 |
java.io.File |
定义jasper文件的文件对象 |
java.lang.String |
定义jasper文件的名字 |
表格9.1
如果这个表达式是一个字符串,它被JRLoader类用来从指定的本地加载文件具体说就是这个字符串首先被解释作为URL,万一MalformedURLException这个字符串被解释成一个文件的物理路径:如果这个文件不存在的话,这个字符串就被解释成java源这就意味着如果你提交一个文件,这个字符串就包含了它的绝对路径,或者你放一个jasper文件在classpath中并提交它用一个java源路径。
我们将先前看到段落落实到实践上。我们想打印一个顺序列表用每个detail行顺序。使用JDBC连接(连接到Northwind数据库):用到的三个表是:Orders, Customers和“Order Details”.打开一个空报表。选择按钮并插入这条查询为父报表:
select * from orders
从这些有效的字段列表中选择ORDERID, ORDERDATE和CUTOMERID字段,以便你的例子简单一些。按OK按钮:这三个字段将被自动的注册到报表中(图9.4和9.5)。
图9.4
图9.5
拖拽这三个字段到detail中,保存报表用你喜欢的名字并在进行子报表之前对它进行测试(图9.6)
图9.6
图9.7
接下来我们就构造第一个子报表来显示以客户顺序来显示客户的信息。Next we will construct the first subreport to display the informationabout the
customer of each order (CUSTOMER).
同样,我们得构造报表控制,准备第二个报表通过添加“CUSTOMERID”参数列表,类型是java.lang.String 。子报表将使用这个参数来查询:
SELECT * FROM CUSTOMERS WHERE CUSTOMERID =$P{CUSTOMERID}.
图9.8
为了能正确的读懂查询字段,需要为参数设置一个缺省值(例如空的字符串“”)
图9.9
在子报表中所有的页的空白都被删除掉,因为这里将插入margins作为一个简单元素。保存子报表(用这个名字命名:customers.jrxml)并在空白处插入子报表元素。垂直尺寸不需要设置,因为打印时JasperReports将需要所有垂直空间(图9.10)。
图9.10
设置子报表的属性,打开元素属性窗口点”Subreport”选项。选择连接模式“Use connection expression”和指定内部参数$P{REPORT_CONNECTION}作为存储JDBC连接的表达式。
图9.11
In the 在“Subreport (Other)”选项中指定找到customer.jasper文件 (包含子报表的文件),以及如何修改参数的边,就是子报表和空白之间。
同子报表表达式一样,通过设定一个简单的jasper文件名之后,通过iReport,我们能确定子报表的位置,因为子报表它的目录被子报表包含在内,将自动的加到CLASSPATH中,所以子报表就能做为一个java源被发现通过ClassLoader 类。这样就需要定义一个子报表目录路径作为参数,定义表达式是:
$P{PATH_TO_SUBREPORTS} +java.io.File.separator +“customers.jasper”.
图9.12
为了能够挑选出这些客户数据和看到他们在子报表中,我们就得填充customers.jasper参数。可以添加一个新行在子报表参数表中,通过指定子报表的参数名和用来为参数创建值的表达式。那么这个参数和表达式合起来就是:$F{CUSTOMERID}。那就是报表空白处相应字段的值。
编译 master.jrxml和customers.jrxml运行父报表
图9.13
如果每步都ok的话,你就能得到像图9.13的东西。接下来,有序的显示第二个子报表在detail中。再一次开始一个空报表,删除空白,定义参数连接到空白处;既然这样ORDERID参数就要声明成java.lang.Integer ,并按顺序插入这条查询: select * from orderDetails where ORDERID = $P{ORDERID}
图9.14
不用设置bands的高度为0 (除了detail之外),在detail中插入我们想打印的字段(图9.15)。
图9.15
加这个子报表到空白处,并定义这新的子报表的文件名,而且要和ORDERID字段捆绑起来。
图9.16
这个文件将成为details.jasper (之前要保存子报表的名字为: details.jrxml).
添加ORDERID的参数用这个值$F{ORDERID}(图9.17)。
图9.17
再次编译和运行整个打印,你将看到像图9.18这样的结果。
图9.18
JasperReports不提供外在机制在子报表填充结束以后来返回从子报表到父报表的参数,然而这个功能可能在许多情形下都要用到,比如你想在子报表中进行计算记录的总数,打印在父报表中,或者在子报表中返回一个有详细细节的结果。在JasperReports所有的表达式必须返回一个对象;由于这个原因像“x = y” (that is an assignment)的表达式就是不允许的;而且父报表和子报表不能共享参数和变量来自于填满的子报表和可以看到结果的父报表。这就需要想一个诀窍来避免这两个问题。事实上,解决的办法要比你想象的简单的多:就是用java.util.HashMap参数来共享一个内存在父报表和子报表之间传递这个参数给子报表,我们通过put值填满HashMap,用预先指定的map指针后退。这中方式我们就不能给参数一个新的值(那是不可能的),但是我们将修改HashMap的内容,我们已经这么做了。
现在就可以找个方法来执行下面的指令:
((java.util.HashMap)$P{ReturnValues}).put(“PROCESSED_RECORDS”,
$V{REPORT_COUNT} )
这个表达式不能够生成值的,所以你可以用一个外部类的公共方法来生成这个表达式的结果并且返回任意值。
我们需要的这个类的简单的代码如下:
public class ReportHelper {
public static boolean (Object map,
String key,
Object value)
{
if (key == null || value == null) returnfalse;
((java.util.HashMap)map).put(key,value);
return false;
}
}
这个类是简单的:The class is simple: 它仅有一个静态方法addMapValue,用来在HashMap中插入一个键,并生成一个boolean值(这个值永远是false)。
用这个方法就可以得到我们想要的值,我们可以虚拟一个元素(也就是一行)加到报表中(概要中的例子)并设置它的printWhenExpression为:
new Boolean (ReportHelper.addMapValue($P{ReturnValues},
“PROCESSED_RECORDS”,
$V{REPORT_COUNT} ) )
不需要关心元素的类型,因为它不会被打印,因为addMapValue返回值永远是false。选择正确的位置,插入元素到报表中,它允许我们无论什么时间,无论多少次addMapValue肯定都执行。
datasource是一个源来自于JasperReports,并取走数据用于打印。这儿有两种类型的datasources:一个是JDBC连接到关系数据库,在这个数据库上执行sql查询,这个对象继承JRDataSource接口,那个,像我们看到的,允许我们去管理详细的数据像这种XML文档或者一个JavaBean。
从一个关系数据(通过sql查询)找回数据来打印的这中能力来创建一个报表是非常简单的,因为可以通过鼠标点击“register”查询字段作为报表字段(没有指定每一个单独字段的名字和类型)。iReport可能关联每一个数据库,为数据库提供一个JDBC驱动,并且提出你配置的用来完成从sql查询开始创建一个报表的向导。当你不想存取数据通过JDBC (或者当你不想JasperReport不直接影响数据库),这时就需要用到JRDataSource (JasperReports数据源),这是一个接口允许存取数据好象他们被构造在表格中,被组织在lines和columns上(lines是datasource指定的记录,columns是记录的字段)。
The JDBC连接或者JRDataSource不是通过JasperReports创建的,但是是通过调用报表的应用程序生成的。这个程序将传递JasperReports的fillReport方法来打开连接到数据库(一个java.sql.Connection对象)或者由于JRDataSource填充报表的情况。
第一个中,JasperReports将使用提供的JDBC连接去执行sql查询指定在报表中(明显的这个查询仅仅在你使用这种方法时想打印报表而指定)。查询的结果被包含在被JRResultSetDataSource的指定的JRDataSource中。以这种方式, JasperReports用JRDataSource对象联合这些数据去打印,表示一般的用这个库管理数据打印的接口。
在这章我们将在iReport的使用和配置时澄清JRDataSource的不同。而且我们将看到如何扩充JRDataSource ;有时扩充datasources需要我们超越JasperReports的限制,就像没有直接的支持交叉报表。
iReport允许我们管理和配置不同类型的datasources来填充报表。以下是存储在iReport中需要我们配置和激活的datasources。
你可能使用到的datasources类型:
- JDBC connection
- XML DataSource
- JavaBean Collection Datasource
- CSV DataSource
- Custom DataSource
在报表生成时JDBC连接被打开并立即传递给JasperReports。XML DataSource 允许我们从XML文档中取数据。CSV DataSource允许你打开一个CSV(以逗号分割的值)文件在报表中使用。JavaBean Collection Datasource和Custom DataSource
允许你打印数据使用java类。Datasources被管理通过菜单 “Datasource → Connections /Datasources“ (图10.1), 打开配置连接列表。
图10.1
技术上connection和datasource是不同的对象 (第一个总是需要一个关系数据库, 但是第二个表示一个简单的接口去存取数据结构在任意的表单中)。然而现在我们将使用这两个相似的单词。
即使我们保持任意数量的datasources准备去用,iReport工作仅仅使用一个源或连接。设置“active” datasource, 选择主窗口菜单“Build → Set the active connection”并选择想要的datasources从提示列表中(图10.2)。
图10.2
如果没有datasource被选择,就不可能为报表提供数据。用报表向导配置一个能起作用的JDBC连接(仅仅一个允许执行sql查询)。
JDBC (Java Database Connectivity)连接允许我们去使用作为一个关系DBMS (或者无论那种更接近通过JDBC驱动的) driver). To set a设置一个新的JDBC连接,按“New”按钮依照图10.1提供的,将打开一个新的界面创建一个新的连接(或datasource)。
图10.3
首先为这个连接命名(可以用一个有意义的名字,像“Mysql – Test”)。iReport将使用这个指定的名字进行连接。
JDBC Driver区域是用来指定连接数据库的JDBC驱动的名字。这个选择框包含了常用的JDBC名字。
iReport仅仅装载了Mysql和HSQLDB的JDBC驱动。
幸亏JDBC URL Wizard, 他可以自动的构造JDBC URL来连接数据库通过插入服务名和数据库名在恰当的textfield。按Wizard按钮创建URL。
插入用户名和密码去存取数据库。意思就是一个选择框可能保存连接密码。
iReport将密码保存在清晰的配置文件并加载到
如果密码是空的,最好保存它。
插入所有的数据以后,需要按“Test”按钮来校验连接。如果一切都ok,将会出现以下提示:
图10.4
测试后,记着设置创建连接作为“active connection”去使用。
如果测试失败,可能是以下这些原因:
- ClassNotFoundException
- URL不正确
- 连接参数不正确(Database not found, user or password wrong, etc...)
这个异常出现当所需要的JDBC驱动在CLASSPATH中不存在,比如我们创建一个Oracle数据库连接。iReport缺省驱动中没有oracle的,但是在JDBC驱动列表中存在oracle.jdbc.driver.OracleDriver驱动,事实上当你测试连接时就会出现这个异常:
图10.5
我们并没有将oracle的JDBC驱动加到CLASSPATH(这里JVM可以搜索到的类)中,它是一个名叫classes12.zip的文件(或者老版本的classes11.zip). 当作iReport使用自己的Class Loader,需要拷贝完整的classes12.zip文件到iReport的lib目录下,然后重新测试就不会发生异常了。
Lib目录是插入jar档案的正确地方(扩展名为jar或zip)。如果这个驱动不是jar版本的,它就被作为未知目录加载(例如,com目录包含所有驱动类的级数),最正确的地方去动态的加这些类到CLASSPATH就需要拷贝驱动目录到classes目录(iReport的主目录)
如果指定一个错误的URL(例如指定一个错误类型的),不会发生什么事当你按“Test”按钮时。事实上会抛出一个StackOverflowError异常(堆栈报告被打印在控制台或者shell中在iReport被打开时)。
既然这样,最好还是用URL向导来建这个JDBC URL,然后再试一次。
这个错误是比较少见点的,它是你在建立数据库连接时用了错误的参数时发生的(用户名或密码无效,数据库不存在,等)。这个错误或多或少是比较直观的连接错误。
当使用JDBC创建报表时,用户指定一个sql查询从数据库中查询记录用来打印。这个连接也能被在子报表或这例如,通过个性化的查找功能为详细的数据码。为这个原因, JasperReports安置我们的配置作为指定的参数,名字叫REPORT_CONNECTION,类型为java.sql.Connection,这个参数能被随意的使用,它的语法格式:
$P{REPORT_CONNECTION}
这个参数包含在正确的java.sql.Connection类传递给JasperReports来自于calling程序。
JDBC 或SQL连接看来是最简单和容易的方法用来填充报表。如何创建sql查询将在第11章解释。
为了能使用sql查询字段在报表中,需要去“注册”他们(需要注册所有已经选择的字段,以便能有效的使用)。每一个字段都需要指定名字和类型。以下是sql类型影射到相应的java类型:
|
|
CHAR |
String |
VARCHAR |
String |
LONGVARCHAR |
String |
NUMERIC |
java.math.BigDecimal |
DECIMAL |
java.math.BigDecimal |
BIT |
Boolean |
TINYINT |
Integer |
SMALLINT |
Integer |
INTEGER |
Integer |
BIGINT |
Long |
REAL |
Float |
FLOAT |
Double |
DOUBLE |
Double |
BINARY |
byte[] |
VARBINARY |
byte[] |
LONGVARBINARY |
byte[] |
DATE |
java.sql.Date |
TIME |
java.sql.Time |
TIMESTAMP |
java.sql.Timestamp |
表格10.1
这个表格中的BLOB和CLOB类型不像其他指定的类型,像ARRAY, STRUCT, REF, 等... 这些类型不能被JasperReports管理(然而可能用他们通过一般的对象声明,也可以管理他们通过写一些支持静态方法。BINARY,
VARBINARY和LONGBINARY类型应该用类似的方法。)可以在java对象中依靠JDBC驱动包含sql类型。
用iReport影射自动的注册sql查询字段更为简单比表中提到的。
进行探索datasources的不同之前,需要理解JRDataSource接口工作。每个JRDataSource都必须实现这两个方法:
public boolean ()
public Object (JRField jrField)
第一个方法是用来移动有用的指针到下一条记录:事实上我们说的那些数据被JRDataSource展示在表格中。Next方法返回true,如果指针正确指到后来的记录上时,但如果指到无效记录上,将返回false。
JasperReports每次执行next时,所有的记录被重新的填充到报表,所有的表达式(从那些关联的变量开始)被适当的计算; 随后将决定是否打印新组的头,以及新页,等。当next返回false时,报表就结束所有bands的打印(group footer, column footer, last page footer and the summary).next能被调用许多次。
getFieldValue is方法在next方法返回true以后被调用。 事实上,getFieldValueis 被执行为每个在报表中显示的字段(看第7章如何声明报表字段)。访问一个已经被传递的作为参数的JRField对象;它就是你像获得的字段值的名字,一个你期望得到的一个java类型对象以及这个字段的描述(不是去指定一些有用的datasource信息来取得字段值)。
getFieldValue方法的返回值的类型被适当的公告在JRField参数中,除非它返回一个null值。这些可能的返回值类型有:java.lang.Object, java.lang.Boolean,java.lang.Byte,java.util.Date,java.sql.Timestamp,java.sql.Time,java.lang.Double,
java.lang.Float,java.lang.Integer,java.io.InputStream, java.lang.Long,
java.lang.Short,java.math.BigDecimal,java.lang.String. 如java.lang.Objectis
被请求作为返回值的类型,这个方法就能返回任意类型。这个例子中的datasource不作为简单类型字段 (作为一个数据库记录的产生), 但仅仅一个字段来自于java对象和被用在表达式中通过一个例子: 在第7章一 字段被描述作为例子:它的语法格式是:
((it.businesslogic.Person)$F{MyPerson})
因为这个字段类型是被解释器知道的类型而被简单的作为Object.
这个datasource允许我们使用一些JavaBeans作为数据来填充报表。JavaBean是一个java类,它里面都是些“getter”方法:
public
为了创建这个类型的连接,选择下图(图10.6)中的第一个选择框的值为“JavaBean set datasource”。
图10.6
再一次要做的第一件事是指定这个新的datasource的名字。JavaBean将一个外部类(被叫做Factory)作为datasource来生成包含为报表提供数据的对象(JavaBean)输入你的java类(全名被指定在Factory class字段中),这个类有一个静态的方法来表示不同的JavaBeans 并返回他们作为收集器(java.util.Collection)或一个数组(Object[])。这个方法的名字和返回类型被指定在这个窗口的其他地方。
让我们看看如何写这个工厂类。假设你的对象是Person ;以下是这个类的代码,包含两个字段:name (这个人的名字) and age.
public class
{
private String name = "";
private int age = 0;
public Person(String name,
{
this.name = name;
this.age = age;
}
public int get()
{
return age;
}
public String get()
{
return name;
}
}
这个类我们命名为TestFactory,它的结果类似于下面的结果:
public class
{
public static java.util.Collection ()
{
java.util.Vector collection = newjava.util.Vector();
collection.add(new ("Ted", 20) );
collection.add(new ("Jack", 34));
collection.add(new ("Bob", 56) );
collection.add(new ("Alice",12) );
collection.add(new ("Robin",22));
collection.add(new ("Peter",28));
return collection;
}
}
datasource将表现5个Person类型的JavaBeans:
Factory name:
Factory class:
Method to call:
Return type:
The peculiarity of a JavaBean设置字段作为datasoiurce,这些字段通过getter方法暴漏出去。set datasource are the fields that are exposed through
the getter methods. 意思就是如果JavaBean有一个getXyz()方法,那么xyz就是一条记录字段(thatis composed by the bean).
这个例子中,person对象中有两个字段:name和age;分别注册他们用string和integer类型在字段列表中。
创建一个新报表,打开值窗口添加这两个字段:
图10.7
移动这些字段到detail中并运行报表。图10.8展示了报表的设计过程,而接下来的图则展示了用javabean设置的字段的打印结果。
图10.8
图10.9
从0.4.0版本开始,iReport支持XML文档的datasource。而先前的版本支持特定的datasource。由于新版本的适应性,老版本就被打成左派。
A XML文档是一个有代表性的树型结构,那么我们就需要去了解已经被选择和提出作为XML文档的节点。这样做还要使用一个XPath (XML Path Language)表达式;通过它的一个节点可以定义。XPath必须有效在http://www.w3.org/TR/xpath页面上。
用一些例子来帮助了解怎样定义节点。考虑一下表格10.2的XML文件。这是一个假设的有不同人的地址薄,他们以类别来分组。
在一种类别列表的最后最喜欢的对象出现。
这个例子可能需要定义不同节点类型,这个类型就是你想组织的报表中的数据的类型。
Davolio
Nancy
Fuller
Andrew
Leverling
Peacock
Margaret
表格10.2
只选择这个种类的人的容器(所有的人在这个addressbook中)的表达式:
/addressbook/category/person
返回节点将是4:
Davolio
Nancy
Fuller
Andrew
Leverling
Peacock
Margaret
表格10.3
如果你想选择你中意的人的节点,表达式该这样写:
/addressbook/favorites/person
返回的节点将是 2:
表格10.4
这儿我们计划一个稍微再复杂一点的表达式比上个例子,为了表现Xpath的能力:这个想法是选择person节点属于work组。表达式是:
/addressbook/category[@name = "work"]/person
这个表达式只返回一个id = 4的节点:
Peacock
Margaret
表格10.5
你已经知道怎样创建节点表达式,接下来创建一个XMLdatasource。
打开创建datasource的窗口,选择“XML Filedatasource”连接。
图10.10
除此之外,还需要填写XML文件名和XPath表达式。
这个例子中XMLdatasource,定义报表需要的字段,除了类型和名字以外,还要插入字段描述的表达式。
作为datasource的目标总是选择设置一个节点,这个表达式是“相关的”和提供的节点。
选择提供节点的属性值的语法是:
@
定义一个字段用来存储一个人的id(这个节点人的属性id),需要创建一个新的节点,并命名一个任意的名字:
@id
一个相似的方法可以得到子节点从提供的节点中。比如你想得到lastname节点,person的子节点,用以下语法:
lastname
移动提供节点的父节点的值 (例如一个人所属的类别的名字) 用有点不同的语法:
ancestor::category/@name
“ancestor”键表示你涉及到的父节点,具体的说是你涉及的第一个父节点的category类型,你想知道的name属性的值。
现在将在行动看到上面的每件事。准备一个简单的报表用已经注册过的字段像表10.6。
|
|
|
ID |
@id |
Integer |
LASTNAME |
lastname |
String |
FIRSTNAME |
forname |
String |
CATEGORY |
ancestor::category/@name |
String |
表格10.6
设置不同的字段到detail band(如图10.11)。表格10.2是要填充报表的XML文件。Xpath表达式:
/addressbook/category/person
所有的结果查看图10.12
图10.11
图10.12
node set 可以识别连续的节点,从JRDataSource点查看一些记录。
然而,如果XML文档非常复杂的话,就需要查看其他的node sets在主节点上。
考虑一下下面的XML,它对表10.2做了一点修改,添加了hobbies节点包含了一个连续的hobby节点,还有一个或更多个email地址。
Davolio
Nancy
Music
Sport
<
Fuller
Andrew
Cinema
Sport
<
Leverling
表格10.7
我们想生成一个比目前看到的更为细致的文档:每个人能查看email地址和hobbies列表。获得这样的文档就需要用到子报表,在这部分你就需要一个你中意的人的email地址列表和hobbies。我们的兴趣可能是了解如何生成一个新的datasource来填充子报表。为这个目的,JRXmlDataSource有两个非常有用的方法:
public JRXmlDataSource (StringselectExpression)
和方法:
public JRXmlDataSource (StringselectExpression)
他们之间的不同主要是前一个是从文档的根节点开始选择,而后一个是从所选择的节点开始。
这两个方法都使用子报表元素的DataSource Expression去生成动态的datasource传递给子报表。最重要的事情不但要我们生成datasource,还需要选择动态的节点表达式。
这个例子中,的表达式能创建这个datasource来提供给子报表email地址:
((net.sf.jasperreports.engine.data.JRXmlDataSource)
).("/person/email")
也就是说 “从现有的节点(person)返回所有的这个person的直接后代email节点”。
这个表达式对于hobbies也是一样的,除非用以下这个方法:
((net.sf.jasperreports.engine.data.JRXmlDataSource)
).("/person/hobbies/hobby")
报表的主字段在表10.6中。在子报表我们提供涉及到的节点值,所以字段表达式将会简化成一个点(.)。
表格10.13
继续建我们的三个报表:addressbook.jasper,email.jasper和hobby.jasper.
表格10.14
在“主”报表中,addressbook.jrxml,我们将插入一个名字叫CATEGORY的组,和它关联的表达式是CATEGORY ($F{CATEGORY})字段。在CATEGORYHeader band插入一个字段,哪儿我们将看到组的名字。照着做,不同的人的名字将被划分为组(就想XML文件中的一样)。在detail中我们安置“ID”, “LASTNAME”和“FIRSTNAME”字段。接下来定位子报表的位置,分别是hobby和后来的email地址。
“email”和“hobby”子报表是一样的,除了表达式中的字段不一样。这两个报表是一样大小的子报表元素在主报表中,空白也都减小到零。
图10.15
编译并执行,如果一切ok的话,我们就能看到类似于图10.16的结果。你可以看到每个人的组在主窗口和工作组以及每个人在子报表中关联的列表中。
图10.16
可以看到XPath 提供的XMLdatasource的强大,它可以操纵节点选择精确的方式。
CSV(Comma Separated Values)文档datasource是我们目前为止看到的最简单的。
创建一个基于CSV文件的连接,打开新建连接窗口选择File CSV DataSource 项。
仅仅需要定义的信息除了datasource的名字外,就是CSV的目录。
图10.17
这个datasource执行起来非常的简单,所以我们在下一段落来看如何应用这个datasource。然而容易却表现出了CSV细节上的不足: 事实上这个datasource不支持mulit-line记录,不管理溢出的序列和顶点,它采取通过“;”分开不同的字节。独立所有的它的限制,这种datasource非常适合用来说教用,它可以很快的在缺乏database时查询出数据达到测试的目的。
这种类型的datasource的所有字段都是string型的,他们能被注册用名字“COLUMN_n”,n表示字段的号(n的最小值是1)。意思就是说如果你有CSV字段包含三个字段,例如:
Davolio;Nancy;[email protected]
Fuller;Andrew;[email protected]
Peacock;Margaret;[email protected]
第一个字段将命名为COLUMN_1, 第二个为COLUMN_2 ,等等。
JasperReports 安排了一个特殊的datasource叫JREmptyDataSource。它是用来创建报表的,当你按下按钮时。这个源的特权是能够返回true给next方法一个记录的号(确省值是1),并返回null,当调用getFieldValue方法时。就像有记录没有字段,一个空的datasource。
两个类构造器:
public (int count)
public ()
第一个可以显示返回多少条记录,第二条显示记录的条数。
有时提供的JRDataSource 并不能完全的满足它的需要。在这些例子中,它可能自治的写一个新的JRDataSource。这个操作并不复杂:事实上所有的类都实现接口JRDataSource(表10.8),包含两个方法:next和getFieldValue。
Package net.sf.jasperreports.engine;
public interface JRDataSource
{
public boolean () throws JRException;
public Object (JRField jrField) throws
JRException;
}
表10.8
next方法是用来通过datasource移动记录显示出来的。如果有新的记录它返回true,反之返回false。
如果next方法被调用,getFieldValue方法将返回需要字段的值(或者null,如果这个值未找到或者不存在)。。具体的说就是请求字段的名字被包含在JRField对象中作为参数传递。。JRField也是一个接口,它可以得到一个字段相关的三个信息:名字,属性,和java类型(我们有一个JRDataSource列表关于这三个类型在这章中)。
现在我们继续写我们个性化的datasource。这个想法比较的新颖,你可以写一个datasource来探索一个文件系统的目录并返回找到的对象(文件或目录)。这些字段我们将处理成我们的datasource:文件名,我们命名为FILENAME,,这有一个标记,就是说如果这个对象是一个文件或目录,我们命名为IS_DIRECTORY ,文件的大小,如果可用的话,我们命名为SIZE。
有两个构造器为我们的datasource:前一个接见作为一个参数目录去浏览,后一个没有参数(将使用以有的目录浏览)。
举个例子,datasource将寻找当前的文件和目录用指定的方法,加载数组文件。
next方法将增加变量的index,我们将保存位置延伸到数组files和返回true直到达到数组的末尾。
import net.sf.jasperreports.engine.*;
import java.io.*;
public class JRFileSystemDataSourceimplements JRDataSource
{
File[] files = null;
int index = -1;
public JRFileSystemDataSource(String path)
{
File dir = new File(path);
if (dir.exists() &&dir.isDirectory())
{
files = dir.listFiles();
}
}
public JRFileSystemDataSource()
{
this(".");
}
public boolean next() throws JRException
{
index++;
if (files != null && index
{
return true;
}
return false;
}
public Object getFieldValue(JRFieldjrField) throws JRException
{
File f = files[index];
if (f == null) return null;
if (jrField.getName().equals("FILENAME"))
{
return f.getName();
}
else if(jrField.getName().equals("IS_DIRECTORY"))
{
return new Boolean(f.isDirectory());
}
else if(jrField.getName().equals("SIZE"))
{
return new Long(f.length());
}
// Field not found...
return null;
}
}
表10.9
getFieldValue方法将返回请求文件的信息。我们不执行关于返回类型的信息通过调用这个方法,但是它返回一个string类型的名字,标记IS_DIRECTORY作为boolean对象,文件尺寸作为long对象。
以下的段落我们将解释如何使用我们的datasource在iReport中,并测试它。
为了iReport datasource是个性化的,当我们指定图形界面来显示它的使用方法是没用的。我们可能说datasource关于所有的个性化除了: - JRXmlDataSource
- JRBeanArrayDataSource
- JRBeanCollectionDataSource
事实上所有的这些都是由iReport自动管理的。
为所有其他的datasource,需要设置一个指定的连接,可以你想要的任一种JRDataSource,通过选择JRDataSource指定的“dirver”种类列表。这些“drivers”是一些简单的java类,他们是用来测试datasource或为报表提供数据,好象被执行通过具体的程序。
这个想法是我们看了JavaBean设置datasource之后有的,其实他们也都一样的:需要写一个java类通过静态方法创建datasource并返回它。
例如如果你想测试,我们有解释过在上一段,就需要创建向表10.10那样的简单的类。
import net.sf.jasperreports.engine.*;
public class TestFileSystemDataSource
{
public static JRDataSource test()
{
return newJRFileSystemDataSource("/");
}
}
表10.10
这个类中的静态方法将被调用时,将执行所有需要的代码来获得合适的datasource。在我们的例子中,我们将创建一个新的JRFileSystemDataSource对象,通过和指定根目录一样的方法(“/”)。
现在我们确定用这种方式来获得JRDataSource ,创建一个连接以备使用。
打开新建连接窗口,选择类型“Custom JRDataSource”,设置这个datasource名字为“Test FileSystemDataSource”(或者其他你愿意的名字)。
图10.18
在这之后,需要定义这个类和方法来获得我们的JRFileSystemDataSource的实例:TestFileSystemDataSource和test.
通过这个datasource准备一个新报表。没有方法可以找到字段通过datasource。这个例子中我们知道管理三个字段和他们的名字和一个类型:一但你创建了这些字段,插入到detail中运行打印:
图10.19
打印已经被创建了。这个报表有被分开成两列,在column header的band中,file name和sizetags****入进去。指定的三个字段也被公告出来,FILENAME和SIZE被立即的插入到detail。然后这个两个位置****入图象,一个表示文档,另一个是打开的文件夹。图形元素被安置在醒目的地方,表达式是: $F{IS_DIRECTORY}
图10.20显示了设计窗口中的图。
图10.20
这个例子中被命名为JRFileSystemDataSource的datasource非常的简单。然而它可能引发更多复杂的类,这个datasource被获得通过一个Enterprise Java Bean或WebService...
iReport提供了详细的datasource,它是JavaBeanExtended DataSource,它表现一个进化的datasource,比起JavaBean的datasource来说。这两datasouce之间的不同可以能就是移动字段在子类中,因此它是可能的,一如,打印一个假想的“street”字段的bean包含在“person”bean中。
图10.21显示了从bean中查找字段的工具,而且很容易使用这些对象
T已选的字段可能被注册为字段。iReport保存必须的方法来查找详细的字段在字段描述中。例如一个Address.Street提供datasource来查找,可以通过调用getAddress().getStreet()方法。
可用的构造器为这个datasource:
public (Vector beans)
public (Object[] beans)
一些请求的对象和字段都被datasource管理。
图10.21
设计者通过表达式来填充子报表。假设person对象有一个getHobbies()方法,这个方法返回类型是hobby对象的携带者,就需要表达式去填充子报表,这个hobby的打印应该是:
newJRExtendedBeanDataSource((java.util.Vector)$F{Hobbies})
Hobbies作为一个java.lang.Object被公告。
JasperReports 0.6.2介绍了一些新报表特点:国际化。国际化的报表意味着所有的静态文本(像labels,messages等)在设计期间可以被修改成locale选项:报表引擎可以用最合适的有效的转化来打印报表。
文本转化在不同的语言支持通过报表被存储在特殊的名为Resorurce Bundle的源文件中。
在这章将介绍内置的功能msg()和如何“本地化“非常复杂的动态创建。
当一个报表是国际化时,就需要定义所有依照文本的地方,像labels和静态strings。键(一个名字)被关联到每个文本片段。这些键和有关系的文本转化被写在指定的文件中(每种语言),像表11.1。
=General Data
=Address
=Name
=Phone
表111.1
所有的包含这些信息的文件被保存以“.properties”文件扩展名。有效的文件名(也就是是没有文件扩展名和语言/国家代码—随后将看到)表示报表Resource Bundle Base Name(例如:源文件i18nReport.properties的Resource BoundleBase Name是i18nReport) 。在执行的时候,报表引擎将查看一个文件,它的名字由Resource Bundle Base Name和扩展名“.properties”构成,在classpath中(所以在先前的例子中它将查看文件名为:i18nReport.properties)。如果这个文件被发现,它将读取本地文本。resource bundle base name可以在报表属性窗口的“i18n”选项卡来指定(查看40页)。
当它被请求使用指定地打印,JasperReports寻找一个以resource bundle base name字符串开头的文件,接下来是请求地的语言和国家代码。例如,i18nReport_it_IT.properties是一个包含意大利本地化字符串的文件,而i18nReport_en_US.properties则被转化成美国英语。所以创建一个缺省的文件,它包含最普遍范围语言和和设置指定文件的语言用其他的语言的文件
缺省的源文件没有语言/国家代码在后,那么将仅仅使用源文件的值在转化文件时。这个源文件的值被使用仅仅如果没有
完整的源文件名应该是这样的:
这是一些有效的源文件名的例子:
i18nReport_fr_CA_UNIX
i18nReport_fr_CA
i18nReport_fr
i18nReport_en_US
i18nReport_en
i18nReport
“other code” (可选择代码), 指语言和国家最右边的代码(例子中的“_UNIX”)是普遍并且也不使用在报表中,但却可以有效识别源文件。
iReport有能力管理源文件的本地化。唯一的情况是被本地化的源文件和本地的jrxml源文件在同一个目录,或者resource bundle basename和jrxml文件名(排除扩展名)相同。
访问报表源文件的列表,选择菜单“View →
Internationalization”然后选择“Localization files”。
图11.1
从这个窗口你可以创建,修改或者删除报表的源文件。
图11.2
创建新的“locale”文件, 需要重写却省文件和修改它的后缀(language/country code),在之后添加base name 从新的完整的源文件名:布置必须依照先前的规则(更多的语言和国家代码和普遍的源文件名可以查看java文档)。
通过按“Modify file”按钮可以编辑选择文件的内容。
图11.3
按“Save”按钮保存修改的源文件,因为这个文件不会自动的被保存。
有两种方法可以找回本地字符串来插入到JaserReport表达式:你可以用内置的str(“key name”)功能或者用指定的语法$R{key name}。源键被转化成java变量在报表编辑期间,像字段,变量和参数:允许使用任意的名字作为源键,不用遵守java变量的规则(就像这样,你可以插入一个点“.”在这些名字中)。总直,最好不要使用空格““字符。这儿是一个表达式例子用来找回本地化字符串:
$R{text.paragraph1}
这个表达式能修改与其相关的文本用这个键“text.paragraph1”,使用最合适的变量转化为选择的地区。
JasperReports国际化的支持基于java。一个最有用的功能就是msg,它被用来动态的生成消息。事实上,msg使用字符串“模式”。 这些模式作为参数传递给msg功能,必须被放置。
安置点用braces之间的数字表示:The program extracted {0} as a random number。 0表示第一个传递给msg功能的值的点。表达式:
msg($R{text.paragraph1}, $P{number})
使用key text.paragraph1提到的这个字符串调用msg。第二个参数是第一个点被取代的string。如果text.paragraph1是字符串,报表参数“号”的值是“100”,然后打印的文本变成:The program extracted 100 as a random number 。
原因是使用生成的message的取代像区分单个的子字符串[The program extracted] {0} [as a random number],有时第二个方法是不可能的。定位器不能被创建以合法的适当的为所有的语言(例如:句子最后的动词的语言等。)
可以通过三种方式调用msg功能:(查看表11.2)。
public String (String pattern, Object arg0)
public String (String pattern, Object arg0,Object arg1)
public String (String pattern, Object arg0,Object arg1, Object arg2)
表11.2
他们之间的不同仅仅是传递点数量不同。
配置本地化报表的第一步,这是必然的对所有的属性文件,包含转变的字符串,且在classpath中。
JasperReports寻找源文件用ResourceBundle java类中的getBoundle方法。想了解更多关于这个类可以查看网站:http://java.sun.com/docs/books/tutorial/i18n/这儿有主要的概念关于java对国际化的支持。
脚本是一个java类,它是用来执行指定的细节在打印期间。脚本中的一些方法被报表引擎的调用,当一些特殊的事件,像创建一个新页或者处理detail 行。
这章我们将介绍如何写一个简单的脚本,并使用在报表中。我们将看看iReport如何的运用脚本,处理一个报表用这种功能的用途。
实现一个脚本就得继承这个java类:
net.sf.jasperreports.engine.JRAbstractScriptlet.这个类含有所有的抽象方法,运用在报表生成期间,提供存取所有的变量,字段和参数的数据结构给报表。
倘若这些简单的脚本直接执行通过JasperReports:它是JRAbstractScriptlet类,继承这个类并实现所有需要的抽象方法用一个没有返回值的方法体。
package net.sf.jasperreports.engine;
/**
* @author Teodor Danciu ([email protected])
* @version $Id: JRDefaultScriptlet.java,v 1.3 2004/06/0120:28:22
teodord Exp $
*/
public class extends JRAbstractScriptlet
{
public JRDefaultScriptlet() { }
public void () throws JRScriptletException
{
}
public void () throws JRScriptletException
{
}
public void () throws JRScriptletException
{
}
public void () throws JRScriptletException
{
}
public void () throws JRScriptletException
{
}
public void () throws JRScriptletException
{
}
public void (String groupName) throws
JRScriptletException
{
}
public void (String groupName) throws
JRScriptletException
{
}
public void () throws JRScriptletException
{
}
public void () throws JRScriptletException
{
}
}
表12.1
像你看到的,这个类是通过使用跟随在event或action名字(DetailEval
or PageInit)的关键字after或者before形成的。这些方法map都能运用于脚本。他们概括如下表。
|
|
Before Report Init |
在报表初始化(也就是所有的变量初始化之前)之前调用。 |
After Report Init |
所有的变量初始化之后调用。 |
Before Page Init |
创建一个新页时,所有变量有重设类型“page”被初始化之前调用。 |
After Page Init |
创建一个新页时,所有变量有重设类型“page”被初始化之后调用。 |
Before Column Init |
被调用当一个新列被创建时,所有变量重设列类型“column”之前,但如果这列已经被添满这个时间将不发生。 |
After Column Init |
被调用当一个新列被创建时,所有变量重设列类型“column”之后,但如果这列已经被添满这个时间将不发生。 |
Before Group |
当一个新组 |
After Group |
当一个新组 |
Before Detail Eval |
在detail band 被打印之前和所有变量新赋值时调用。 |
After Detail Eval |
在detail band 被打印之前和所有变量赋当前值时调用。 |
表12.2
在脚本内我们提供所有的字段,变量和参数使用以下的maps(java.util.HashMap),来定义类的属性:fieldsMap,variablesMap和parametersMap.
组(如果要显示在报表上的话)能被存取通过属性groups,一个JRFillGroup数组。
如果我们需要为某些报表创建脚本的话,我们可以使用iReport运用它。
图12.1
这个例子中类实现了脚本,将被iReport完全运用。我们可以不使用脚本通过设置报表属性窗口(图12.1)或者指定一外部类(已经编译且放到classpath中)。在这个例子的最后,如果这个类被修改和重新编译,iReport将不可能使用我们最近编译的版本,取决与java类装载器,类在内存中的缓存。
如果脚本已经运用与iReport中,当报表被编译,一个和报表名字一样的新类,后缀是“Scriptlet.java”的文件被创建。例如,如果你有一个test报表,生成的脚本文件的名字就是testScriptlet.java.。
使用iReport生成的脚本不直接的继承JRAbstractServlet 。
代替他们继承的是一个名叫IreportScriptlet的高水平的类,它在it.businesslogic.ireport包。除了这么多方法来处理报表之外,一个新类可以提供一些有用的特性来处理这一连串的数据(查看14章);其他的特性将在未来的iReport版本中添加进去。请注意,如果你想用iReport提供的图表,你得用内在支持的脚本。
在iReport中可以通过选择菜单“View → Scriptlet Editor”来修改脚本。脚本源文件是独立与主报表源文件的,所以你需要在每次改变它之后按 “Save”按钮保存它(查看图12.2)。
图12.2
这个编辑窗口显示了脚本的所有方法/事件,在表单的上面的选择框中。左边显示了所有和脚本代码有关的java对象。双击设计对象,显示相应的代码。可以插入import标识,脚本中新的方法和类成员,选择“
public String numberToRoman(int myNumber)
这个语法就是用来转化的,调用这个方法插入到脚本中:
((it.businesslogic.ireport.IReportScriptlet)$P{REPORT_SCRIPTLET}
).numberToRoman ( < my number >)
请注意,我们需要一个外部的引用来调用这个方法numberToRoman ,因为报表引擎将$P{REPORT_SCRIPTLET}只看做一般的脚本。
在这个例子中,我们假设这个类:
it.businesslogic.ireport.IReportScriptlet包含一个名叫numberToRoman的方法。事实上并不是这样的,这儿没有一个numberToRoman方法在这个类中。你得计算REPORT_SCRIPTLET到你的真实的脚本类中
((MyScriptletClassName)$P{REPORT_SCRIPTLET}).numberToRoman( <
my number >)
当报表和脚本一起被编译时,脚本被编译保存到和用来编译报表的jasper文件相同的目录。iReport将这个目录添加到classpath中作为确省的。以这种方式脚本类也将能看到从java和报表中。然而,当报表被配置(就像web application)时,脚本通常会被忘记,那么报表引擎就会抛出一个错误当填充报表时。所以,也需要将脚本类放到应用程序的classpath中。如果脚本继承IReportScriptlet类,你也必须将这个it.businesslogic.ireport.IReportScritplet类(这个类被发布在LGPL许可下)
也添加到classpath中。
iReport中最有用的工具之一就是使用报表模板wizard来创建报表,一种pre-built模型作为基础来使用。这章我们将介绍如何建一个通俗模版和如何添加他们到那些已经使用的。
一个模版是一个正规的jrxml文件。当创建写报表时用到这个向导,jrxml文件和被选择模版被加载和修改依照用户的选择。这儿有两种类型的模版:columnar 和tabular 类型。模型创建一组线为每个单独的记录成为静态文本,显示文件名,而textfield显示字段值(图13.1)。
图13.1
二选一,tabular类型显示所有的记录像表一样(图13.2)。
像我们说的那样,模版是jrxml文件(扩展名是简单的xml),他们被加载到templates目录。iReport承认这个名字,如果一个文件包含columnar或tabular的模版:如果一文件名以T结尾,将被用作tabular,如果以C结尾将被解释为columnar。
以下是iReport加载的模版列表:
|
|
classicC.xml |
Columnar report |
classicT.xml |
Tabular report |
classic_landscapeT.xml |
Tabular report |
graycC.xml |
Columnar report |
grayT.xm |
Tabular report |
gray_landscapeT.xml |
Tabular report |
表格:13.1
这个向导允许4组以上的创建 (每组的 GroupHeader和Group Footer是相互关联的).这些组将被创建用一个加长的完整的文件仅仅如果在向导执行期间用户用一个或多个标准来分组数据。用这个向导你只能选择一个字段名作为标准。
打开classicC.xml文件,就可以清晰的看到模版的构造。文件包含了四个组:Group1,Group2, Group3和Group4 ,分别是:title band(groupheader)和group footer显而易见。Columnsband是隐藏的(因为columnar 报表中的这个band没用),detail有静态的文本标签作为模版的标签,以便每个预备字段就像图13.1,textfield包含真正的字段。Text(或者textfield形式的表达式)依照简单的向导规格关联到text元素,具体的说就是每个组可以包含许多你想要的图形元素和包含简单文本的静态文本元素:
GnLabel
n表示文本元素被放置的地方的组的号,textfield元素包含以下简单的表达式:
GnField
这个元素用组表达式包含值。
图13.2
Detail必须包含最少的文本元素:
DetailLabel
Textfield元素使用表达式:
DetailField
向导复制这两个元素,创建和选择的报表字段一样多的静态text/textfield对。
所有其他的bands能包含任何元素;这些bands在文件从模板生成时将被复制。
Tabular报表模板的设计是一样的。图13.4显示classicT.xml如何生成。
一旦一四个组。他们有column header,需要插入将带的文本来使用作为模型为columns labels。
在detail band中有仅仅一个DetailField元素,它将作为所有columns的模型。
图13.3
Templates不可能被编译,因为在textfields中这些表达式不是有效的java表达式。
所以,让我们看看如何创建一个通用模板(custom template)。最简单的方法就是打开一个现有的模板,选择一个和我们想法接近的。这时我们就可以编辑报表以我们喜欢的方式,改变现有的元素属性或添加和删除其他元素。
图13.4
为了使用这些模板,你必须放它到templates目录。记着用.xml作为文件后缀。就像图13.5那样命名文件名testC.xml 。记着在写后缀之前将C或T作为文件名的最后一个字符。
如果每件事都ok,通过执行向导,就能看到新的模板在columnar报表模板列表中(图13.6)。
图13.5
然而你可能已经看到了,我们的新custom template没有预览。可能只有查看一个最大150x150像素的gif格式的图片。(这个例子中是testC.gif)。
图13.6
图13.7向导窗口显示了事先的图片为我们的新模板。这个事先图片来自于报表创建时的一个屏幕截图。
模板的使用相对而言提高了报表开发的效率,通过共享图片。
如果你开发了一非常久经世故模板,你想共享它给其他的用户,你就可以将它上传到iReport网站上。
JasperReports不是天生就支持显示图表的:他们被单独的生成,用一个或多个java open-source库来生成图表和作为一个图形元素来显示图片。这个想法非常的简单,然而制作图表在run-time就需要非常好JasperReports 设计技术。需要用脚本来收集显示在图表上的数据。
使用0.4.0版本,iReport有一个无返回值的图表工具。用这个工具就可以通过配置主要的属性和查找数据来打印图表,更加的简单化。图表的创建就完全依靠一个名叫JFreeCharts(0.9.21版本)的java open-source库,它是由Object Refinery Limited的David Gilbert开发的,仅有少数的图表属性,但可以创建一个清晰的报表。
这段我们就来学习chart工具一步一步的创建一个包括3D饼图表,然后我们分析所有的chart管理的细节。
这个例子我们使用HSQLDB中的Northwind数据库作为datasource。
创建新的空白文档,打开查询窗口并输入:
select , COUNT(*) AS from ORDERS
group by SHIPCOUNTRY
图11.4
这个想法是制作一个不同国家的销售图表。确定我们的查询ok:iReport将注册选择的字段。拖拽他们到detail中。(图14.1)
图14.2
重设bands的高度(除了summary和detail)。
选择chart工具并放置一个新的chart到summary中。
图14.3
iReport将提示你是否需要内部脚本处理:你选择是。从图表窗口选择饼图并按ok按钮。你将看到图14.2的情形。
接下来配置图表,打开元素属性窗口(双击元素),移动到“Chart”选项卡上,选择“Edit chart properties”按钮。
图14.4
将看到图表管理窗口(同时显示图表元素创建,图14.5)。
图14.5
这个窗口由三个选项卡组成:Chart type,Data和Chart details. 第一个可以选择图表的类型:每个图表需要组织级别数据;级别需要通过chart information选择框中列出来的图表。
警告!每次选择不同的图表类型,那么插入到detail选项中data和chart都将被覆盖掉。
这个例子中我们需要标签(Labels)的级数和一个级数值(Serie1)。
移动到Data选项卡。你将看到两行用来输入这两个级的名字,以便满足已选图表的需要(图14.6)。
图14.6
有许多方法可以创建series。这时我们就用最简单:我们许可iReport管理它为我们。选择“Report series”按钮连接到报表的series管理窗口(图14.5)。
图14.7
创建一个新的series用“New series”按钮。在图14.8中将看到。指定series名字,设置“Reset When”为
图14.8
为了用表达式编辑,在表达式编辑上点鼠标左键选择“
一旦你添加了两个series,回到图14.6窗口选择新的series列表:选择SERIE_COUNTRY作为Labels series和SERIE_ORDERS_COUNT作为Serie1。
警告修改了图表,保存文件和并开始报表用按钮。结果显示在图14.9。
图14.9
Series表示一些由字符串或数字值组成的一组值。每个图表需要两个或多个数据的series以便更形象。当你选择图表类型(图14.5),窗口底部的表单可以指定已选择的的图表的series。如果你需要一个饼图(一种图表类似于饼的,我们在先前段落中看到的),需要两个series,分别是Labels和Series1:这个表单包含了不同图表“片”的标签,后面的值表示片。通常当series的标签是“Labels“时,iReport期望series被composed通过设置string对象,另外还有numeric对象(像double或integer)。所有的series关联到同一个图表,并有同一个元素号码。
Series是一个简单的java vector(java.util.Vector)。它可能让iReport自动重建一个或多个series(查看前一段)通过脚本;如果你想更多的控制series(例如当你想创建图表在报表的不同地方),可能要手工的来管理,通过使用IReportScriptlet类的一些方法来放置series。
IReportScriptlet提供一个方法来得到series内容:getSerie()。这个表达式是用来打印series的内容到textfield:
""+((it.businesslogic.ireport.IReportScriptlet)
$P{REPORT_SCRIPTLET}).(“
getSerie方法返回对象,它里面包含了由空字符串联起来的字符串内容。图14.10显示了使用getSerie的例子:每次迭加期间,series对象和和包含在里面的值被打印出来。
图14.10
自动series是被iReport完全的管理。仅仅需要用户做的一件事就是定义你想生成的series的值的表达式。例如你想收集到值假设通过一个字段,那么series的表达式就应该是这样的:
$F{MyField}
MyField是一个字段名。
要创建自动的series,选择菜单View → Report Serie. 这种方式可以得到公告在报表中的series的列表。按New series按钮来创建一个新的series,依照这章前面的例子来创建。
automatic series 存储了从datasource读取的行的数目的一个数字,或者是最后的重设置的series。如果“Reset when“值为series是
如果automatic series创建机制不适合去创建我们感兴趣的收集(例如,你想创建一个group subtotals 的series),就需要手工填充一些series。Series仅仅是一个简单的Vector:IReportScriptlet处理你的配置两个方法来管理Vector,你的series将表现的。这个方法是:
public Boolean (String serieName, Objectvalue)
public Boolean (String serieName)
前一个方法允许你添加一个值到指定的series,后一个方法允许你删除series包含的所有的值。
这两个方法都返回boolean对象的值为false。在JasperReports中不可能执行任意的没有用脚本的java指令(像需要填充series的)。然而,下面我们将介绍一个比较游泳技巧来避免这种局限性。这个想法就是在报表中插入一个假的元素,它将永远不会被打印:在这个元素的“printWhenExpression”将执行返回对象值是false的代码。这儿是一个可能的表达式:
((it.businesslogic.ireport.IReportScriptlet)
$P{REPORT_SCRIPTLET}). (“TEST”,”ciao”)
这个结果是一个值为false的boolean对象,但是当你用方法添加一个新的元素到“TEST”series时,将被初始化。Manualseries没有在任何地方被公告;他们被创建了,如果不存在,在第一次运行方法。
现在,让我们看一个简单的例子来模仿这种automatic series的手动机制;创建一个空白文档并设置查询SELECT * FROM ORDERS;
表ORDERS有不同的字段,我们想收集记录中的SHIPCITY字段的假值,并保存名为“TEST”的series的顺序。在detail中插入一小行(line元素),将元素的PrintWhenExpression写入。
((it.businesslogic.ireport.IReportScriptlet)
$P{REPORT_SCRIPTLET}).("TEST",$F{SHIPCITY})
选择属性以便JasperReport在没有插入line元素时正常运转。
在summary插入一个text元素,那儿将表示已经创建的series。就这样做,用这个表达式为被指定为“TEST”的series的打印:
""+((it.businesslogic.ireport.IReportScriptlet)
$P{REPORT_SCRIPTLET}).(“TEST”)
结果将是一个空白的detail在报表的最后,所有的城市列表将按照已选择的顺序打印。
通常,一个元素被加到series,这个series被指定当JasperReports求printWhenExpression的值时,在这儿将插入调用的方法,此时假元素也包含在PrintWhenExpression中被报表引擎遇到;在这个例子中,这个元素通过放置在detail band的line表现。配置同样的元素在不同的band(例如page footer),那么series也就不同了(具体来说就是它被composed通过每页最后一行的元素)。
除非允许准确控制当添加元素到series时,用这个表达式加这个值在这种情形下,
考虑一下下面的表达式例子:
new Boolean(
($F{SHIPCITY} != null &&$F{SHIPCITY}.length() > 3) &&
(((it.businesslogic.ireport.IReportScriptlet)
$P{REPORT_SCRIPTLET}).("TEST",$F{SHIPCITY})
.booleanValue()))
以上可以简化为:
new Boolean(
($F{SHIPCITY} != null &&$F{SHIPCITY}.length() > 3)
FALSE通常调用addValueSerie方法。这两个表达式都是true的话就返回true,如果JVM校验
Series被识别为简单的字符串,series号也是任意定义的。
“ghost”元素能欺骗addValueSerie方法的执行来重设series的值,通过调用方法。
这些调用被引见在表达式编辑器的规则中(图14.11)。
图14.11
iReport允许管理六种不同类型的图表,他们中的每一中通过一些属性来定义,这些属性中的一些能定义所有的图表类型,另一些是各自的特性。
如果series表现数据来画一个图表,这些属性可以修改它的样子,可以在图表属性窗口(图14.12)的“Chart Details”选项卡来修改。
图14.12
以下是对所有图表的通用属性的概括。
|
|
|
Width |
图表的像素宽度,通常也是元素的宽度 |
|
Height |
图表的像素高,通常也是元素的高度 |
|
Zoom |
表示图表图象质量的要素:预置值是2,也就是图表图片的高度和宽度都为2。 |
|
Chart title |
图表的标题,如果不指定,图表就没有标题 |
|
Subtitle |
图表的副标题,如果不指定,图表就没有副标题 |
|
Title position |
依照图表确定标题的位置;能值是: Top, Bottom, Left和Right和显示图象的位置: |
| |
|
Chart Background |
图表的背景色(包括画title,legend, 等时) |
|
Plot Background |
图表的背景色(图表轴之间的扁平区域) |
|
Antialias |
指定是否使用平滑 |
|
Show tooltips |
指定是否显示tooltip,这是一个****的标签用来显示图表点的值(或者是用来说明饼图的薄片) |
|
Show legend |
指定时候显示图例 |
表格14.1
饼图是最简单的图表在iReport中,它可以形象化的用数字表示series()用series标签()。它没有除了公共属性以外的特殊属性。
图14.13
3D饼图和饼图是一样的,只是使用了三维效果。
图14.14
|
|
Depth factor |
表示饼图的高度值(图14.14使用的值是0.2) |
Foreground Alpha |
图表的透明度(图14.14使用的值是0.33) |
在iReport中除了饼图(简单和3D),其他所有图表都使用CategoryDataset数据结构,它是以种类组成的一组series值。为柱状图表,也为其他图表,需要三级值:, 和。他们被解释以下面的方法:每个和series相关的作为同一类别。如果你仅仅想要一组值,series是一样,而类型是变化的。
|
|
|
1.0 |
Serie 1 |
C1 |
4.0 |
Serie 1 |
C2 |
3.0 |
Serie 1 |
C3 |
5.0 |
Serie 1 |
C4 |
5.0 |
Serie 1 |
C5 |
7.0 |
Serie 1 |
C6 |
7.0 |
Serie 1 |
C7 |
8.0 |
Serie 1 |
C8 |
如果你保持category的数量和改变series,你将有仅仅一个标签(category值)和一种颜色为每个series:
|
|
|
1.0 |
Serie 1 |
Type 1 |
4.0 |
Serie 2 |
Type 1 |
3.0 |
Serie 3 |
Type 1 |
5.0 |
Serie 4 |
Type 1 |
5.0 |
Serie 5 |
Type 1 |
7.0 |
Serie 6 |
Type 1 |
7.0 |
Serie 7 |
Type 1 |
8.0 |
Serie 8 |
Type 1 |
如果你想更形象化不同categories,你需要指出series和categories所属的每个值。
|
|
|
1.0 |
Serie 1 |
Type 1 |
4.0 |
Serie 2 |
Type 1 |
3.0 |
Serie 3 |
Type 1 |
5.0 |
Serie 4 |
Type 1 |
5.0 |
Serie 1 |
Type 2 |
7.0 |
Serie 2 |
Type 2 |
7.0 |
Serie 3 |
Type 2 |
8.0 |
Serie 4 |
Type 2 |
通常这对(Value, Category)被看成X和Y轴的值。这个series允许为同一个category设置同一个值。
|
|
Plot orientation |
Bars方向;以前例子中预先设置的值是vertical,这儿是一个水平方向的例子: |
Foreground Alpha |
图表的透明度(适合具有彩色背景) |
Value label |
Label为axe(XY轴)的值 |
Category labela |
Label 为categories axe |
Bar3D图表和Bar的特性一样,除了具有三维外表。
图14.15
线型图表被penalized(处罚)通过使用CategoryDataset ,因为因为最后一个不工作和x,y相陪的值,在Y轴和X轴的“categories”。
图14.16
和柱状图一样,每个series有相应的颜色,线在“categories”没有有意义数字时被断开。
这个问题将在未来的iReport版本中解释XYDataset的Line Chart。
Line chart和柱状图有一样的附加属性。
最后一个是区域图。它和Line chart工作原理一样,但X轴和线之间的区域被完全的涂上颜色。通过适当的选择Foreground Alpha属性,可以使图表非常的独特。
图14.17
此时iReport管理JFreeChart的可能性很小:图表的特性将在未来发布的版本中管理的越多越好。
它可能扩充iReport的功能依靠plugins,外部的应用程序模型设计来执行多方面的任务,比如安置一个元素用特殊标准,或者将编译的jasper文件放到BLOB数据库中。
Plugins在iReport启动时被加载通过选择菜单“Plugins”(图15.1)。
图15.1 图15.2
图15.3
选择菜单“Tools → Plugin configuration”可以打开插件列表配置他们(图15.3)。
每个插件被创建通过继承抽象类it.businesslogic.ireport.plugin.IReportPlugin和写一个XML文件包含插件展开的指示。这个类必须添加到classpath中,XML文件也必须放到iReport根目录下plugin目录下。所有的XML文件在这个目录被处理来启动相应的插件。
iReport伴随两个插件Massive compiler和Text Wizard,此外还有一个简单的“Hello World!”插件作为例子。
插件结构XML文件,使用展开的描述符,是很简单的(这儿是DTD描述他们的标记)。
name NMTOKEN #REQUIRED
class NMTOKEN #REQUIRED
loadOnStartup (true | false)"false"
hide (true | false) "false"
configurable (true | false)"false">
根元素是iReportPlugin以及他们的属性:The root element isiReportPlugin and here are theirattributes:
Name 是插件的名字;
class 是实现插件的java类;它必须放到和所有必须的类一样都添加到classpath中;如果需要一个jar来运行插件,你还得把它放到iReport的lib目录下;
loadOnStartup iReport保持在内存中的一个plugin实例:设置属性为true就可以强制iReport在启动时实例化plugin,而不需要等待用户的指令;
hide 如果设置为true,这个属性能被隐藏在plugin中(将被隐藏在所有和插件有关的菜单入口);
configurable 如果设置为true,iReport将激活“Configure”按钮为plugin,在窗口显示图15.3。
如果你想指定作者的名字为plugin(,现在不使用Author标签),一个小plugin描述(标签描述)和一个16x16像素图标,这个图标被使用在菜单中:自图标文件被加载作为java源,这标签的值必须是classpath中的路径(例如:/it/businesslogic/ireport/icons/menu/new.gif)。
以下是XML文件用来激活“Example1”插件以便iReport加载。
name=""
class=""
loadOnStartup=""
hide = ""
configurable = "">
要卸载插件,只要删除plugin目录下的XML文件即可(或者改变它的扩展名,只要和.xml不同就可以)。
最困难的部分就是讲述plugin程序的编写。你需要创建一个新的java类继承it.businesslogic.ireport.plugin.IReportPlugin类。这个类包含两个“切入点”,他们是configure和 call方法;第一个仅仅在plugin有“configurable”标签时被调用,和更正确当用户使按钮可见时图15.3(the user hits the button
“Configure”visible on figure 15.3)。第二个方法是在用户选择菜单中plugin名字时被调用(图15.1)。
这儿是IReportPlugin类的源代码。
package it.businesslogic.ireport.plugin;
importit.businesslogic.ireport.gui.MainFrame;
/**
* This class must be extended by alliReport plugin.
* To install a plugin into iReport, put theplugin xml in the plugin
* directory of iReport.
* See plugin documentation on how to createa plugin for iReport
* This is the first very simple interfaceto plugin. I hope to
* don't change it, but we can't say whatit'll happen in he future...
*
* @author Administrator
*/
public abstract class IReportPlugin {
MainFrame mainFrame = null;
String name = "";
/**
* This method is called when the plugin isselected from the plugin menu
*/
public abstract void ();
/**
* This method is called when the pluginconfiguration button on plugin
* list is selected.
* Configuration file of plugin should bestored in
* IREPORT_USER_HOME_DIR/plugins/
*/
public void (){}
/**
* Retrive the plugin name. Please note thatthe plugin name must be
* unique and should be used as filename forthe configuration file if
* needed. This name can be different fromthe name specified in XML,
* that is the name used for the menu item.
*/
public String (){
return name;
}
/** Getter for property mainFrame.
* @return Value of property mainFrame.
*
*/
publicit.businesslogic.ireport.gui.MainFrame getMainFrame() {
return mainFrame;
}
/** Setter for property mainFrame.
* @param mainFrame New value of propertymainFrame.
*
*/
public void setMainFrame(
it.businesslogic.ireport.gui.MainFrame mf){
this.mainFrame = mf;
}
}
你已经看到了,仅仅有一个抽象方法call。事实上,configure方法是被实例化了(没有返回体),这是因为没有更多意义来强迫用户去实例化它当用户没有使用插件时(也就是当插件不是可配置的)。
这是个好想法来定义一个插件构造器,不需要争议和设置一个值为这个类的name属性。Plugin一被实例化,iReport就调用plugin的setMainFrame方法来填充mainFrame属性,这个属性涉及到iReport的核心类:通过这个类你可以访问报表,你可以编译,你可以修改iReport的结构等等。
现在我们把焦点放到方法call上。我们已经谈论过这个情况,iReport为每个plugin创建并保存一个实例在内存中:意思就是说call方法不是线程安全的。这就是建议我们继承IReportPlugin的类应该是一种容器为真正的plugin;call方法应该在入口出运行plugin代码。根据这点可以用两种方式来描述plugin:一个是简单的持久实例化,但要避免调用两个连续的plugin,另一个是多重的“不稳定的“实例化,在plugin代码结束时消失:意思就是说当plugin每次被执行时创建一个新的实例,当call方法被实例化时。
以下是一个plugin用持久实例化工作的例子清单。
public class MyPlugin extends IReportPlugin{
MyPluginFrame frame = null;
Public MyPlugin()
{
setName(“my sample plugin”);
}
/**
* This method is called when the plugin isselected from the plugin menu
*/
public t void ()
{
if (frame == null)
{
frame = new MyPluginFrame();
}
if (!frame.isVisible())frame.setVisible(true);
}
}
这个例子中的MyPluginFrame类是plugin的核心类,它展示在任何时间,当用户从菜单上选择这个plugin时。Massive compilerplugin就是以这种方式工作的。 Plugin窗口能被打开关闭很多次,但却从来不会分开,永远保持一个状态。
如果你想创建一个不同与plugin核心类在plugin每次被执行时被实例化,需要对call方法做一些修改:
public class MyPlugin extends IReportPlugin{
Public MyPlugin()
{
setName(“my sample plugin”);
}
/**
* This method is called when the plugin isselected from the plugin menu
*/
public t void ()
{
MyPluginFrame frame = new MyPluginFrame();
frame.show();
}
}
这个例子中,当用户启动这个plugin时,一个新的MyPluginFrame类型的窗口将被打开。
这里包含了plugins的所有信息,我们使用一部分和iReport有联系的代码:
MainFrame mf = MainFrame.getMainInstance();
这种用法返回一个和MainFrame有关的对象;
mf.getActiveReportFrame();
这个调用返回现行的报表窗口(JReportFrame),从这个窗口可以通过调用getReport()方法找到报表对象。
我们非常支持开发新的插件。如果你有任何关于这个话题的建议,或者可以澄清如何实现一个新的插件,不要害怕和我们联系。:)
Massive compiler是一个编译大型jrxml文件的工具。它是有用的当你想迁徙到一个新的JasperReports版本:在这个例子中,为避免版本冲突,你还得重编译老的源文件。
图15.4
当plugin被启动时,将显示一个窗口用来选择编译的文件。你可以输入或者用“browse“按钮选择存储在编译目录中的文件(你可以强制plugin在子目录中查找,通过选择可选框” Search Sub Directories “)。按”find“按钮,将列出所有一.jrcml或.xml结尾的文件。我们仅仅选择这些文件来编译,通过按“Compile selected file(s)”按钮。
如果你想你可能替代这个老的.xml文件用新的.jrxml文件。如果.jasper文件已经存在详细的文件,它可能会创建一个备份,创建新报表之前拷贝老文件。
如果有错误放生当编译文件时,一个警告图标显示在左边的文件名上。双击连接到这个叫人厌恶的文件,你就能看到错误细节(图15.5)。
图15.5
plugin Text Wizard被创建是用来简单化文本报表的生成(基于字符的报表)。目的是用标签和字段建一个列表格式的报表。
当你启动这个插件,他确定打开的报表的宽度和改变宽度。通过按“Check forfields widths”按钮,包含变量的报表字段被刷新。字段名字的右边,有其他两列;第一个包含了最小值,就是需要显示的全部内容的字段的数目(这个信息可以通过用JDBC驱动的ResultSetMetaData返回来得到);第二列可以手动的改变字段的最小长度:这个值被切断后的所有字符。
按“Add elements”按钮添加标签和字段安置到报表中。每一个Textfield都将设置一个表达式来截短包含的值,就像这样:
( ((!=null) && (.length() > )) ?
.substring(0,) : )
这个例子上的字段,XYZ可能有一个最小长度值是50个字符。
图15.6
如果字段的值的长度超过50,它将被截短。