JasperReports+Hibernate轻松解决Web开发中的数据报表问题

一、在Java应用程序中打印报告
如今,在应用程序开发过程中报告模块经常变得越来越庞大和复杂。当客户们意识到报告提供给他们的优点时,他们往往要求报告模块提供给他们更多的信 息。报告模块的开发就是在这样一种“事后回想”的环境下成为应用程序的一个重要的组成部分。另一方面,报告模块往往是在应用程序开发的后期附加上去的而不 是在初始开发阶段就被纳入考虑并被实现。
最近,我在开发一些基于Apache POI库的应用程序,其中大量地使用到XLS文件的报告输出。我很快意识到:这些报告模块能够绑定大量极有价值的开发资源以便于将来之用。当客户端要求输 出PDF格式时,最初的对于iText API的研究使我偶然发现了JasperReports。最后,我确信,JasperReports一定会极大地影响到我们小组开发报告的方法。
在实现JasperReports之前,创建每一种报告都要求基于Apache POI库开发一个定制报告类。这种方法要耗费大量宝贵的开发时间来创建报告的许多方面,例如单元格特定的格式化,风格及填充方法等。而如今, JasperReports能够帮助我们的开发小组夺回宝贵的开发时间,却仍然能够实现与原先同样的报告,因为这个工具嵌入式地使用了Apache POI库。
引入JasperReports的一个重要优点是,可以通过单个报告模板生产出若干种格式的报告。这意味着,针对于XLS格式输出创建的模板还能够用来生成PDF文件,甚至是CSV,HTML或XML文件。
二、JasperReports如何帮助开发者?
JasperReports使开发者能够快速而容易地创建出以多种格式输出的报告。开发者还能够在设计或运行时刻使用JasperReports引 擎编译报告模板,从而允许生成动态的报告格式。而且,开发者还能够把来自于许多种数据源的数据导入到这些报告中。如今,开发者再也不必依赖于Apache POI来创建定制报告类或者使用iText库来格式化报告和确定报告风格,从而使他们专注于报告的数据检索。最终,通过在应用程序开发中使用 JasperReports,开发者实现了极有价值的灵活性并节约了开发时间。
JasperReports所使用的XML报告模板提供了布局和描述信息,这些信息用于格式化结果报告以及域、变量和参数等参考内容。借助于某种第 三方GUI(例如iReport)再加上极少的开发者协助,即使是非开发人员也能够创建这些模板。这样以来,开发者就不必自己去实现报告布局及描述内容。
总之,JasperReports能够使开发者专注于开发他们的报告模块部分,从而使之不必再编写定制的报告生成代码。在整个报告模块中,开发者的角色被缩减到模板编译、数据源实现和报告创建等几个方面。
三、创建和编译XML报告模板
JasperReports要求使用一种定义布局、描述及数据域的报告设计。可以使用 net.sf.jasperreports.engine.design.JasperDesign对象来构建这种设计;这样以来,开发者能够动态地创建 报告设计或通过由一个XML报告模板创建一个net.sf.jasperreports.engine.design.JasperDesign实例的方 式来创建报告设计。除非一个应用程序特别要求使用一种动态的布局;否则,一般推荐使用一个编译的XML报告模板。这种XML模板通常以一个.jrxml文 件扩展名保存并且使用net.sf.jasperreports.engine.JasperCompileManager进行编译。
这个JasperReports XML模板中包括了相应于、、、的元素以及主要的数据元素。每一个这些元素都有相应的各种子元素。从列表1(请参考源码文件)的sampleReport.jrxml中可以清楚地看出这一点。
另外,从这个sampleReport.jrxml文件中还可以看出,有些元素(例如和)中包含了布局信息,而其它一些元素(例如和)中则包含了描述信息。该XML模板还包含、和元素以用于包括报告中的数据。
其中,元素允许可以把非数据源信息(例如一个动态标题)输入到一个报告中;元素是唯一的把报告域映射到数据源域的方法,而是运行时刻生成以用于报告中的值。关于JasperReports XML报告模板的完整的文档类型定义(DTD)可以在《JasperReports最终指南》中找到。
注意,我们可以使用JasperReports Ant任务(作为一个Ant构建的部分)在运行时刻或构建时刻编译XML模板。
在运行时刻编译报告需要把该报告加载到一个JasperDesign对象并且使用创建的实例作为到 JasperCompileManager.compileReport(JasperDesign design)方法的参数—这个方法返回一个JasperReport实例。作为选择,XML模板还能够被传递给 JasperCompileManager.compileToFileReport(String sourceFileName)方法,这个方法能够创建一个在整个程序中使用的编译的报告文件(.jasper)。
使用JasperReports Ant任务在构建时刻编译报告要求把相应的任务定义添加到build.xml文件,还要添加一个相应的使用这个任务的目标(见源码文件中的列表2)。使用 该Ant任务能够在destdir任务中创建一个编译的(.jasper)文件并且通过把true值传递给目标的keepjava属性以便保存Java源 文件。本文相应的源码中包括了一个说明如何使用Ant任务的更为详细的示例。
四、使用数据源填充JasperReports
大多数报告都使用数据库作为数据源,但是JasperReports能够使用任何可用的数据源。这些数据源被作为参数传递给一个 net.sf.jasperreports.engine.JasperFillManagerfillReportXXX()方法。这些方法实现了对两 种类型的数据源的支持—net.sf.jasperreports.engine.JRDataSource和java.sql.Connection。 注意,本文源码文件中包含了两种示例:扩展JRDataSource的静态数据源示例和实现JDBC连接的动态数据源的示例。
其中,StaticDataSource类的实例必须要实现 net.sf.jasperreports.engine.JRDataSource接口,从而支持它填充报告数据—这是通过调用 JasperFillManager.fillReport(JaperReportreport,Map parameters,JRDataSource dataSource)方法来实现的。StaticDataSource中另外两个需要实现的方法getFieldValue(JRField jrField)和next()(它们都由JRDataSource接口提供)负责把数据由数据源传递到JasperReport。 StaticDataSource所使用的数据源是一个静态二维数组(每一个数组项相应于一个玩滚球的人的名字及其在三场游戏中的得分,见源文件中的列表 3)。当处理包含此数据源的fillReport()方法并且在报告中遇到一个详细节(detail section,报告结构的一部分)时,要调用next()方法。如果在数据数组中还存在另一个元素,则在StaticDataSource中的这个方法 实现(见列表4)将返回true;而如果不再有其它数据,则返回false。如果这个方法返回true,那么在详细节中遇到的域元素将激活对 StaticDataSource中的getFieldValue(JRField jrField)方法的调用。在StaticDataSource中的这个方法(见列表5)将返回被映射的数据域名相应的值(相应于数据数组的当前索 引)。当到达详细节的结尾部分时,再次调用next()方法;这一过程将重复执行,直到next()方法返回false为止。
JDBCDataSourceExample(见列表6)例子中实现了一个fillReport()方法,该方法接受一个java.sql.Connection参数。通过把一个元素添加到XML报告模板(jdbcSampleReport.jrxml)中,这个fillReport()方法支持从一个关系数据库中取得数据。这个元素能够返回数据域,以便应用于报告数据映射中—在这种情况下,查询仅是简单地返回sample_data表格中的所有记录。可以在报告模板中使用一个java.sql.ResultSet来代替对元素的实现,从而允许实现动态的查询。
五、Hibernate与JasperReports联用
Hibernate是当前最流行的ORM工具之一。当从一个Hibernate查询中返回一个对象集合时,使用Hibernate作为JasperReports的一个数据源是很简单的;而当返回一个对象元组时,要求实现一个定制的JRDataSource。
当一个Hibernate查询返回一个对象集合时,可以使用 net.sf.jasperreports.engine.data.JRBeanCollection-DataSource把Hibernate POJO实例域映射到相应的报告域。对于这种简单的方案中,我们只需要使用JRBeanCollectionDataSource (java.util.Collection beanCollection)构造器,把在SimpleHibernateExample中实现的Hibernate查询结果集传递给它(见列表7)即 可。在这个示例中,这个简单的使用session.createQuery("from SampleData").list()实现的Hibernate查询与在JDBCDataSourceExample中的实现的那个查询是一致的。 JRBeanCollectionDataSource就象StaticDataSource一样实现了JRDataSource,但是它的 getFieldValue(JRField jrField)方法实现能够把报告模板域名称映射为查询结果bean中相应的属性。
当一个Hibernate查询返回一个对象元组时,必须编写一个JRDataSource的定制实现,这类似于 HibernateDataSource(见源文件列表8)。如果在Hibernate查询结果集中存在另一个列表项,那么,这个类中的next()方法 将返回true,同时把当前列表项放到一个currentValue变量中以便用于getFieldValue(JRField jrField)方法中。通过调用getFieldIndex(String field)方法,getFieldValue()方法可以取得在currentValue对象中的域索引。这个方法将遍历传递到 HibernateDataSource构造器中的被映射的域名称,直到它找到它所传递的域名为止,然后把这个域的索引返回到currentValue变 量中。然后,这个getFieldValue()方法把这个索引中的值返回到currentValue结果对象中。
此外,还有其它的有关于Hibernate和JasperReports联用的方案,这包括在HibernateDataSource中所使用的使用映射来代替名字映射的方法,你可以在Hibernate网站的[url]www.hibernate.org/79.html[/url]处 找到此方法。在此站点上,还有一篇值得我们感兴趣的有关于实现报告优化的题目(请参考John Ferguson Smart 的文章“Hibernate Querying 103: Using Hibernate Queries with JasperReports")。
六、在Web应用程序中把报告导出到PDF和XLS格式
在编译和填充完一个JasperReport报告后,导出它就很简单而且很直接了。为此,我们只要使用它提供给我们的 net.sf.jasperreports.engine.JRExporter接口。借助于JRExporter接口的适当实现, JasperReports能够把数据从同一个报告设计导出到PDF、XLS、CSV、RTF、HTML和XML等多种格式。其中,PDF和XLS格式是 两种最为普通的导出格式(本文源码中都提供了相应的示例实现)。其中,PrintServlet示例能够把数据导出为PDF,而 DataExtractServlet则可以把相同的数据导致到一个XLS格式的文件中。
PrintServlet(见源文件列表9)是一个示例servlet实现类,它使用JasperReports把一个报告导出为PDF格式。 JasperReports利用了开源iText PDF创建库来生成PDF格式的文件。一旦报告在PrintServlet中得到编译,即可创建PDF文件,然后把它传送到Web浏览器以备通过 runReportToPdfStream(InputStream inputStream,OutputStream outputStream,Parameters params,Connection connection)方法(由JasperRunManagerfacade类所实现)实现最终的打印输出。
DataExtractServlet(见列表10),这个示例servlet实现类使用JasperReports把一个报告导出为XLS格式。 注意,JasperReports利用了Apache POI库来生成XLS格式的文件。一旦报告在DataExtractServlet中得到编译,即可在内存中创建XLS文件并且向用户显示一个保存对话 框。这个servlet使用了net.sf.jasperReports.engine.export.JRXlsExporter (JasperReports提供的JRExporter接口的具体实现)来导出报告。导出报告所使用的参数都使用 JRXlsExporterParameter变量加以初始化—设置填充报告 (JRXlsExporterParameter.JASPER_PRINT)和输出流 (JRXlsExporterParameter.OUTPUT_STREAM)。在此,这个输出流实际上就是设置了内容类型和头部的Response对 象;所以,当我们在PrintServlet示例中调用exportReport()时,用户可以保存而不是显示该文件。
【提示】默认情况下,JasperReport把页眉放在每一个数据报告页面的顶部。当导出到一种XLS格式时,这将会中断包含多个单数据报告页面的工作表中的连续数据。当然,通过把输出格式的类型作为一个参数传递到一个报告模板并结合一个元素(基于放在元素中传递的参数),还能够进一步维持这种数据连续性。下面的将导致仅页眉部分被输出到一个报告页面中—如果报告正在处理第一个页面,而此时输出格式不是PDF但每一个报告页面却都是针对PDF输出格式的话。



七、总结
最后,非常希望本文能够引起你对使用JasperReports生成报告的兴趣。当然,如果你已经在使用JasperReports,那么,希望本 文也能提供一些有关于如何创建定制数据源或使用新的导出格式化的建议。注意,一定要理解和彻底掌握JRDataSource中的next()和 getFieldValue(JRField jrField)方法的用法,因为这样以来,你就可以把任何数据源应用于JasperReports报告的生成之中。
借助于其它一些有用的工具,JasperReports报告的创建过程将更为简单。例如,iReport就是一个优秀的JasperReports 模板创建工具,可以应用于一个GUI应用程序中以允许可视化报告设计,从而使一名非开发人员也可以用来创建JasperReport设计。另外,这个工具 还提供针对开发者的其它一些丰富的功能,例如,从一个应用程序外部创建报告预览的数据源连接功能。JasperAssistant,尽管不是开源的,但是 也作为一个Eclipse插件用于以一种类似的GUI方式来开发JasperReport模板,尽管此工具更多地面向开发者设计。上面这两个工具都有助于 报告设计的准备工作;然后,该设计即可以提供给开发者使用,从而使其摆脱掉冗长而乏味的报告生成描述工作。
注意,本文并没有深入地讨论JasperReports的使用及功能,但我希望本文已经向Java开发者介绍了一种强有力的报告开发工具。另外, JasperReports甚至还能够用来生成图表与图形(也可以把图像包括到报告中),从而提高一个应用程序报告系统的丰富性及描述能力。总之, JasperReports是一种强有力的API,借助于它web报告系统开发技术将得到进一步的发展。
















本文转自朱先忠老师51CTO博客,原文链接:http://blog.51cto.com/zhuxianzhong/60132 ,如需转载请自行联系原作者



上一篇:拐弯抹角——实验吧


下一篇:py的交易——实验吧