14.7. JasperReports

JasperReports ( http://jasperreports.sourceforge.net ) 是一个功能强大,开源的报表引擎, 支持使用一种易于理解的XML文档创建报表设计,并可以输出4种格式的报表:CSV、Excel、HTML和PDF。

14.7.1. 依赖的资源

应用程序需要包含最新版本的JasperReports(写本文档的时候是 0.6.1)。 JasperReports自身依赖于下面的项目:

  • BeanShell

  • Commons BeanUtils

  • Commons Collections

  • Commons Digester

  • Commons Logging

  • iText

  • POI

JasperReports还需要一个JAXP解析器。

14.7.2. 配置

要在 ApplicationContext中配置JasperReports,你必须定义一个 ViewResolver来把视图名映射到适当的视图类,这取决于你希望输出什么格式的报表。

14.7.2.1.  配置ViewResolver

通常,你会使用ResourceBundleViewResolver 来根据一个属性文件把视图名映射到视图类和相关文件:

<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
    <property name="basename" value="views"/>
</bean>

这里我们已经定义了一个 ResourceBundleViewResolver 的实例, 它将通过基名(base name) views 在资源文件中查找视图映射。 这个文件的详细内容将在下节内容叙述。

14.7.2.2.  配置View

Spring中包含了JasperReports的五种视图实现,其中四种对应到JasperReports支持的四种输出格式,另一种支持在运行时确定输出格式。

表 14.2.  JasperReports View Classes

类名 渲染格式
JasperReportsCsvView CSV
JasperReportsHtmlView HTML
JasperReportsPdfView PDF
JasperReportsXlsView Microsoft Excel
JasperReportsMultiFormatView 运行时确定格式(参考 第 14.7.2.4 节 “ 使用 JasperReportsMultiFormatView

把这些类映射到视图名和报表文件只需要简单地在前述资源文件中添加适当的条目。如下:

					simpleReport.class=org.springframework.web.servlet.view.jasperreports.JasperReportsPdfView
					simpleReport.url=/WEB-INF/reports/DataSourceReport.jasper
				

这里你可以看到名为simpleReport 的视图被映射到JasperReportsPdfView 类。 这将产生PDF格式的报表输出。该视图的url属性被设置为底层报表文件的位置。

14.7.2.3. 关于报表文件

JasperReports有两种不同的报表文件:一种是设计文件,以 .jrxml 为扩展名;另一种是编译后的格式,以 .jasper 为扩展名。 通常,你使用JasperReports自带的Ant任务来把你的 .jrxml 文件编译为 .jasper 文件,然后部署到应用中。 在Spring里你可以把任一种设计文件映射到报表文件,Spring能帮你自动编译 .jrxml 文件。 但你要注意, .jrxml 被编译后即缓存起来并在整个应用活动期间有效,如果你要做一些改动,就得重启应用。

14.7.2.4.  使用 JasperReportsMultiFormatView

JasperReportsMultiFormatView 允许在运行时指定报表格式, 真正解析报表委托给其它JasperReports的view类 - JasperReportsMultiFormatView类简单地增加了一层包装,允许在运行时准确地指定实现。

JasperReportsMultiFormatView 类引入了两个概念:format key和discriminator key。 JasperReportsMultiFormatView 使用mapping key来查找实际实现类,而使用format key来查找mapping key。 从编程角度来说,你在model中添加一个条目,以format key作键并以mapping key作值,例如:

					public ModelAndView
					handleSimpleReportMulti(HttpServletRequest request,
					HttpServletResponse response) throws Exception {

					String uri = request.getRequestURI(); String format
					= uri.substring(uri.lastIndexOf(".") + 1);

					Map model = getModel(); model.put("format", format);

					return new ModelAndView("simpleReportMulti", model);
					}
				

在这个例子中,mapping key由 request URI的扩展名来决定,并以默认的format key值 format 加入了model。 如果希望使用不同的format key,你可以使用 JasperReportsMultiFormatView 类的formatKey属性来配置它。

JasperReportsMultiFormatView中默认已经配置了下列mapping key:

表 14.3.  JasperReportsMultiFormatView默认Mapping Key映射

Mapping Key View Class
csv JasperReportsCsvView
html JasperReportsHtmlView
pdf JasperReportsPdfView
xls JasperReportsXlsView


所以上例中一个对/foo/myReport.pdf的请求将被映射至 JasperReportsPdfView。 你可以使用 JasperReportsMultiFormatViewformatMappings属性覆盖mapping key到视图类的映射配置。

14.7.3.  构造ModelAndView

为了以你选择的格式正确地渲染报表,你必须为Spring提供所有需要的报表数据。 对JasperReports来说,这就是报表数据源(report datasource)和参数(report parameters)。 报表参数就是一些可以加到model的 Map中的简单键值对。

当添加数据源到model中时,有两种选择。第一种是以任意值为key,添加一个 JRDataSourceCollection 到 modelMap 。 Spring将从model中找到它并用作报表数据源。例如,你可能这样构造model:

					private Map getModel() { Map model = new HashMap();
					Collection beanData = getBeanData();
					model.put("myBeanData", beanData); return model; }
				

第二种方式是以一个特定键值添加 JRDataSourceCollection 的实例, 并把该它赋给视图类的 reportDataKey 属性。 不管哪种方式,Spring都会把 Collection 实例转化为 JRBeanCollectionDataSource 实例。例如:

					private Map getModel() { Map model = new HashMap();
					Collection beanData = getBeanData(); Collection
					someData = getSomeData(); model.put("myBeanData",
					beanData); model.put("someData", someData); return
					model; }
				

这里你可以看到有两个 Collection 实例被加到model里。 为了确保使用正确的那个,我们得适当地改动一下视图的配置:

					simpleReport.class=org.springframework.web.servlet.view.jasperreports.JasperReportsPdfView
					simpleReport.url=/WEB-INF/reports/DataSourceReport.jasper
					simpleReport.reportDataKey=myBeanData
				

注意当使用第一种方式时,Spring将使用它遇到的第一个 JRDataSourceCollection 。 如果你要放置多个这样的实例到model中,你就得使用第二种方式。

14.7.4. 使用子报表

JasperReports提供了对嵌入在主报表文件中的子报表的支持。有多种机制支持在报表文件中包含子报表。 最简单的方法是在设计文件中直接写入子报表的路径和SQL查询。 这种方法的缺点很明显:相关信息被硬编码进报表文件,降低了可复用性,并使报表设计难以修改。 为了克服这些,你可以声明式地配置子报表,并为其包含更多直接来自controller的数据。

14.7.4.1. 配置子报表文件

使用Spring时,为了控制哪个子报表文件被包含,你的报表文件必须被配置为能够从外部来源接受子报表。 要完成这些你得在报表文件中声明一个参数,像这样:

						<parameter name="ProductsSubReport"
						class="net.sf.jasperreports.engine.JasperReport"/>
					

然后,你用这个参数定义一个子报表:

						<subreport> <reportElement
						isPrintRepeatedValues="false" x="5" y="25"
						width="325" height="20"
						isRemoveLineWhenBlank="true"
						backcolor="#ffcc99"/> <subreportParameter
						name="City">
						<subreportParameterExpression><![CDATA[$F{city}]]></subreportParameterExpression>
						</subreportParameter>
						<dataSourceExpression><![CDATA[$P{SubReportData}]]></dataSourceExpression>
						<subreportExpression
						class="net.sf.jasperreports.engine.JasperReport">
						<![CDATA[$P{ProductsSubReport}]]></subreportExpression>
						</subreport>
					

这样就定义了一个主报表文件,接受一个名为 ProductsSubReport 的参数, 它的值是一个 net.sf.jasperreports.engine.JasperReports 类型实例。 然后配置Jasper视图类时,你通过使用 subReportUrls 属性来告诉Spring载入一个报表文件并作为子报表传递给JasperReports引擎。

						<property name="subReportUrls">
						<map> <entry key="ProductsSubReport"
						value="/WEB-INF/reports/subReportChild.jrxml"/>
						</map> </property>
					

这里Map 中的key对应于报表设计文件中子报表参数的名字,它的值代表子报表文件的URL。 Spring将载入该文件,需要的话进行编译,然后以给定的key为参数名传递给JasperReports引擎。

14.7.4.2. 配置子报表数据源

当使用Spring配置子报表时,这一步完全是可选的。如果你喜欢,仍可以使用静态查询作为子报表的数据源。 然而,如果你希望Spring把你返回的 ModelAndView中的数据转化为 JRDataSource, 你就得告诉Spring ModelAndView 中的那个参数需要被转化。 实际操作时,你需要使用所选视图类的 subReportDataKeys 属性,为其配置一个参数名列表:

						<property name="subReportDataKeys"
						value="SubReportData"/>
					

这里提供的key值必须与ModelAndView和报表设计文件中使用的key值对应。

14.7.5. 配置Exporter的参数

如果你对exporter的配置有特殊要求,比如你可能要求特定页面尺寸的PDF报表, 那你可以在Spring配置文件中声明式地配置这些,通过使用视图类的 exporterParameters 属性, 该属性是Map 型值,其中的key应该是一个代表exporter参数定义的静态域的全限定名,value是要赋给参数的值。 示例如下:

				<bean id="htmlReport"
				class="org.springframework.web.servlet.view.jasperreports.JasperReportsHtmlView">
				<property name="url"
				value="/WEB-INF/reports/simpleReport.jrxml"/>
				<property name="exporterParameters"> <map>
				<entry
				key="net.sf.jasperreports.engine.export.JRHtmlExporterParameter.HTML_FOOTER">
				<value>Footer by Spring!
				&lt;/td&gt;&lt;td
				width="50%"&gt;&amp;nbsp;
				&lt;/td&gt;&lt;/tr&gt;
				&lt;/table&gt;&lt;/body&gt;&lt;/html&gt;
				</value> </entry> </map>
				</property> </bean>
			

这里你可以看到,我们为 JasperReportsHtmlView 配置了一个exporter参数, 参数 net.sf.jasperreports.engine.export.JRHtmlExporterParameter.HTML_FOOTER 定义了结果HTML中的页脚。