jasperreports_JasperReports:棘手的部分

jasperreports

如果您使用Java进行编程的时间足够长,则有可能需要为业务用户生成报告。 就我而言,我已经看到几个项目使用JasperReports®Library来生成PDF和其他文件格式的报告。 最近,我荣幸地观察了Mike和他的团队使用上述报告库以及他们所面临的挑战。

简而言之JasperReports

简而言之,使用JasperReports(JR)生成报告涉及三个步骤:

  1. 加载已编译的报告(即,加载JasperReport对象)
  2. 通过用数据填充报告来运行报告(结果是JasperPrint对象)
  3. 将已填充的报告导出到文件(例如,使用JRPdfExporter导出到PDF)

在Java代码中,看起来像这样。

JasperReport compiledReport = JasperCompileManager.compileReport(
        "sample.jrxml");
Map<String, Object> parameters = ...;
java.sql.Connection connection = dataSource.getConnection();
try {
    JasperPrint filledReport = JasperFillManager.fillReport(
            compiledReport, parameters, connection);
    JasperExportManager.exportReportToPdf(
            filledReport, "report.pdf");
} finally {
    connection.close();
}

多亏了facade类,这看起来很简单。 但是外表可能是骗人的!

鉴于以上代码段(以及概述的三个步骤),您认为哪些部分需要最多的时间和内存? (听起来像面试问题)。

如果您回答(#2)填写数据,那是对的! 如果您回答了#3,那也是正确的,因为#3与#2成正比。

恕我直言 ,大多数在线教程仅显示简单的部分。 在JR的情况下,似乎缺少关于较困难和棘手的部分的讨论。 在这里,与Mike的团队一起,我们遇到了两个困难:内存不足错误和长期运行的报告。 使这些困难特别令人难忘的是,它们仅在生产期间出现(而不在开发期间出现)。 我希望通过共享它们,将来可以避免它们。

内存不足错误

第一个挑战是报告内存不足。 在开发过程中,与实际操作数据相比,我们用于运行报告的测试数据将太小。 因此, 为此设计

在我们的例子中,所有报告都使用JRVirtualizer运行。 这样,当达到内存中的页面/对象最大数量时,它将刷新到磁盘/文件。

在此过程中,我们还了解到需要清理虚拟机。 否则,周围会有几个临时文件。 而且,我们只能在报告导出到文件清理这些临时文件。

Map<String, Object> parameters = ...;
JRVirtualizer virtualizer = new JRFileVirtualizer(100);
try {
    parameters.put(JRParameter.REPORT_VIRTUALIZER, virtualizer);
    ...
    ... filledReport = JasperFillManager.fillReport(
            compiledReport, parameters, ...);
    // cannot cleanup virtualizer at this point
    JasperExportManager.exportReportToPdf(filledReport, ...);
} finally {
    virtualizer.cleanup();
}

有关更多信息,请参见Virtualizer Sample – JasperReports

请注意,当我们在运行报告时遇到内存不足的错误时,JR 并不总是罪魁祸首。 有时,即使在使用JR之前,我们也会遇到内存不足错误。 我们看到了如何滥用JPA来加载报告的整个数据集( Query.getResultList()TypedQuery.getResultList() )。 同样,由于数据集仍然很小,因此在开发期间不会显示该错误。 但是,当数据集太大而无法容纳在内存中时,我们会遇到内存不足错误。 我们选择避免​​使用JPA生成报告。 我猜我们只需要等待JPA 2.2的Query.getResultStream()可用即可。 我希望JPA的Query.getResultList()返回Iterable 。 这样,就有可能一次映射一个实体,而不是整个结果集。

现在,避免加载整个数据集。 一次加载一个记录。 在此过程中,我们返回了良好的JDBC。 不错,JR很好地使用了ResultSet

长期运行的报告

第二个挑战是长期运行报告。 同样,在开发过程中可能不会发生这种情况。 充其量,将运行10秒钟左右的报告视为冗长。 但是,有了实际的运行数据,它可以运行大约5-10分钟。 当根据HTTP请求生成报告时,这尤其麻烦。 如果报告可以在超时时间段内(通常为60秒或最多5分钟)开始写入响应输出流,那么它很有可能被请求用户(通常是通过浏览器)接收。 但是,如果填写报告需要5分钟以上,而导出到文件又需要8分钟,那么用户将只会看到超时的HTTP请求,并将其记录为错误。 听起来有点熟?

请记住,报告可以运行几分钟。 因此, 为此设计

就我们而言,我们在单独的线程上启动报告。 对于使用HTTP请求触发的报告,我们将以一个包含所生成报告链接的页面作为响应。 这样可以避免超时问题。 当用户单击此链接而报告尚未完成时,他/她将看到仍在生成报告。 但完成的报告时,他/她就可以看到生成的报告文件。

ExecutorService executorService = ...;
... = executorService.submit(() -> {
    Map<String, Object> parameters = ...;
    try {
        ...
        ... filledReport = JasperFillManager.fillReport(
                compiledReport, parameters, ...);
        JasperExportManager.exportReportToPdf(filledReport, ...);
    } finally {
        ...
    }
});

我们还必须添加停止/取消运行报告的功能。 好东西,JR有检查Thread.interrupted()代码。 因此,仅中断线程将使其停止。 当然,您需要编写一些测试来进行验证(期望JRFillInterruptedExceptionExportInterruptedException )。

在讨论的同时,我们重新发现了将“监听器”添加到报告生成中的方法(例如FillListenerJRExportProgressMonitor )并为用户提供一些进度信息。

我们还创建了实用程序测试类,以通过反复重复给定的数据来生成大量数据。 这对帮助团队的其他成员开发专为处理长期运行和内存不足错误而设计的JR应用程序很有用。

进一步的设计考虑

要考虑的另一件事是填写报告时需要打开和关闭所需的资源。 这可以是JDBC连接,Hibernate会话,JPA EntityManager或文件输入流(例如CSV,XML)。 下图是我的设计注意事项的粗略草图。

1. Compiling
         - - - - - - - - - - - - - -\
         - - - -\                    \
2. Filling       > open-close         \
         - - - -/   resource           > swap to file
                                      /
3. Exporting                         /
         - - - - - - - - - - - - - -/

我们想隔离#2并定义装饰器,这些装饰器将打开资源,填充报告并在finally块中关闭打开的资源。 打开的资源可能取决于报表中的<queryString>元素(如果存在)。 在某些情况下,如果没有<queryString>元素,则可能无需打开资源。

<queryString language="hql">
    <![CDATA[ ... ]]>
</queryString>
...
<queryString language="csv">
    <![CDATA[ ... ]]>
</queryString>

此外,我们还希望将#2和#3组合为一种抽象。 这种单一的抽象使通过装饰进行装饰变得更加容易,例如将创建的页面对象刷新到文件中,并在导出过程中将其加载回。 如前所述,这是JRVirtualizer工作。 但是,我们希望使用结合#2和#3的抽象对对象透明的设计。

致谢

目前为止就这样了。 再次感谢Mike和他的团队分享了他们的经验。 是的,他是将自己应用的收入捐赠给慈善机构的那个人 。 另外,还要感谢克莱尔(Claire)通过一次又一次重复给定数据进行测试的想法。 相关代码段可以在GitHub找到

翻译自: https://www.javacodegeeks.com/2018/01/jasperreports-tricky-parts.html

jasperreports

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值