1. 引言
JasperReport是一个强大、灵活的开源报表生成工具,适用于各种Java应用程序,是当前Java开发者最常用的报表工具之一 [1] 。利用JasperReport生成报表,首先需要设计报表模板,然后对报表模板进行编译、填充等过程,生成最终的报表 [2] 。如果需要修改报表的数据,一般情况下需要修改报表模板,再重新编译,这种方法有两个缺陷:一是报表模板的修改需要写SQL语句进行数据的查询,因此只有懂数据库的程序员才能够修改报表模板,普通的用户是无法操作的;二是在Web报表的实现中,由于报表的生成是在服务器上进行的,因此需要上传修改后的报表模板到服务器,才能生成报表 [3] 。虽然已经有一些方法利用网页的二次开发,能够在网页上修改报表模板 [4] ,但是工作量巨大,仍然要写SQL语句查询数据,普通用户还是无法操作。上述的缺陷主要是由报表模板的变化带来的。针对这一情况,本文利用JavaWeb编程,通过对报表工具JasperReport的定制,设计一个相对通用的报表模板,实现了在网页上定制报表的功能。
2. JasperReport工作方式的选择
2.1. 报表生成过程
JasperReport是用Java开发的报表工具,能够展示丰富的页面内容,并将之转换成PDF、HTML、XLS等格式。IReport是为JasperReport设计的报表可视化设计器,用于生成JasperReport所使用的报表模板 [5] 。IReport支持可视化、拖拽式的开发方式。因为JasperReport本身并未提供可视化报表设计工具,IReport的出现正好弥补了这个缺陷。
使用JasperReport生成报表的过程如图1所示,共有以下四个步骤 [6] :使用IReport设计报表模板,文件后缀是jrxml;编译并校验报表模板,JasperReport读取jrxml文件,验证格式,输出jasper文件;选择数据源,填充数据,形成有数据的报表,输出实例化的JasperPrint类;输出报表,JasperReport可以输出多种形式的报表,如PDF、HTML、XLS等。
2.2. 数据源的选择
2.2.1. 以数据库作为数据源
以数据库作为数据源的方式如图2所示,即在报表模板中定义SQL语句,用于直接查询数据库。后台只需要事先提供数据库与报表之间的连接即可 [1] 。这种方式获取数据效率高,且不需要后台代码的支持,所以被广泛运用。但是由于报表工具对数据的处理不够灵活,所以缺乏一些定制的功能。
2.2.2. 以JavaBean作为数据源
以JavaBean作为数据源的方式如图3所示,后台作为数据库与报表之间的媒介。后台先获取数据库的数据,经过拼装打包成需要的数据,以JavaBean的形式传给报表。这种方式效率不高,但是可以通

Figure 1. The process of the report generation
图1. 报表生成过程

Figure 2. Taking database as a data source
图2. 数据库作数据源方式

Figure 3. Taking JavaBean as a data source
图3. JavaBean作数据源方式
过后台处理数据,因此适用一些需要定制报表的场合。
当要生成数据不同的报表时,以数据库作为数据源,只需要修改报表模板即可,但是报表模板的修改比较复杂,而且需要写SQL语句,所以只适合程序员修改。而以JavaBean作为数据源,可以让不懂数据库的用户在前端页面配置数据,通过后台拼装数据,完成相同的目标。但是需要一些后台代码和前端页面的配合。本文选择以JavaBean作为数据源。
3. 数据库设计
3.1. 生产报表的内容
生产报表的内容如图4所示。每个报表记录的是若干机台在某几天的产量和利用率的情况,其中:
。
由于机台的产能是固定的,维护在机台信息中,因此机台利用率就可以通过产量和机台信息计算出来。
3.2. 数据库表的设计
我们使用MySql数据库,设计了表1~表3,分别存储报表信息、机台信息和产量信息。
由于我们原本就需要对查询所得的数据在后台进行拼装,那么为了减少记录的数量,同时使报表的每条记录的信息更加直观,所以部分的一对多关系我们采用字符串的方式进行存储,如所选机台序列、所选日期序列等。上述的表中,表2机台信息是事先录入在数据库的,表3产量信息是由机台每日上传的。表1则是用户自己定制的,用户可以选择某几个机台和某几个日期,来生成对应的报表。

Figure 4. A sample of production report
图4. 生产报表的内容

Table 1. Report information table (t_report)
表1. 报表信息表(t_report)

Table 2. Machine information table (t_mashine)
表2. 机台信息表(t_mashine)

Table 3. Production information table (t_data)
表3. 产量信息表(t_data)
4.报表实现
4.1. 数据包格式
由2.2.2可知,我们选择JavaBean作为数据源,即后台先获取数据库的数据,经过拼装打包成需要的数据,以JavaBean的形式传给报表。结合3.1生产报表的内容,拼装得到的JavaBean如图5所示。
其中,有三个对象,分别是ReportBean、ItemBean和ValueBean。一个ReportBean包含若干ItemBean,一个ItemBean包含若干ValueBean。ReportBean是报表类,包含了标题title、日期栏columns、机台信息items和合计栏totals。ItemBean是机台信息类,包含了机台编号mashine、机台产能capacity和产量信息values。ValueBean是产量信息类,包含了日期time和产量value。
4.2. 报表模板的设计
设计一个通用的报表模板是解决问题的关键 [7] 。这个报表模板应该能自动适应数据包的长度并且将数据填写到正确的位置上。由上述的分析可知,机台信息、日期栏、合计栏、产量信息这几项属于ArrayList,长度是可变的。设计的报表模板如图6所示。
其中有标题title字段,而日期栏、机台栏、合计栏、机台统计图栏均以子报表的形式存在,每一个子报表都能展示一组循环的数据,通过子报表的嵌套,可以看出类的嵌套关系。我们以机台栏的子报表为例,进行分析。机台信息items的子报表如图7所示。
它是纵向循环的,其中有mashine字段,而机台产量capacity由于只做计算而并不在报表中显示,因此作为变量传递到产量信息的子报表中。而产量信息的子报表如图8所示。
它是横向循环的,其中有产量字段,而机台利用率_value则是由value/capacity计算而来。
从上面的分析,我们可以看出,在报表模板中,利用子报表可以显示循环的数据,利用子报表的嵌套关系可以解析数据包中类的嵌套关系。因此,可以让报表模板适应数据的长度并正确地显示循环数据。
5. 测试
5.1. 测试前提
程序上使用Spring MVC + MyBatis的框架,并且融入了报表工具JasperReport [8] 。已经完成了下列代码:前端页面可以选择机台和日期。后台从数据库获取数据并拼装成JavaBean,传递给报表。报表模板已经完成,报表模板经过编译和填充,能够生成报表文件。
机台数据已经存储在数据库中,如表4所示。以第一条数据为例,表示ID为1的机台编号为M1_001,产能为2000。
产量数据也已经存储在数据库中,如表5所示。同样以第一条数据为例,表示ID为1的记录,其中机台ID为1,日期为2016年7月10日,产量为1840。
5.2. 测试目标
利用同一个报表模板,生成两张格式相同、数据不同的生产报表。第一张报表名为测试报表1,内容为机台M1_001至M1_004在7月10日至7月16日的产量。第二张报表名为测试报表2,内容为机台M1_005至M1_006在7月12日至7月19日的产量。

Figure 7. Subreport of machine information
图7. 机台信息子报表
5.3. 测试过程
打开报表管理的界面,新增第一张报表,名称为测试报表1,机台选择M1_001至M1_004,日期选择7月10日至7月16日,点击保存,如图9所示。
类似的方法,在不修改报表模板的情况下,新增第二张报表,名称为测试报表2,机台选择M1_005至M1_006,日期选择7月12日至7月19日,点击保存,如图10所示。
分别预览测试报表1、2,得带如图11、图12的两张报表。
我们通过前端页面上的选择,利用一个通用的报表模板,实现格式相同数据不同的两张报表的生成,实现了测试的目标。
6. 结束语
本文在报表工具JasperReport的基础上,以设计一个相对通用的报表模板为核心,选择JavaBean作为数据源,通过数据库设计、后台代码实现与前端页面的配合,使用户在不需要修改报表模板的情况下,通过前端页面得到自己想要数据的报表,实现了生产报表的定制。本文是JasperReport的一种改进方式,
也为Web报表的定制提供了一种新的思路。