如果您具有业务应用程序开发的经验,那么很可能会遇到要求该应用程序具有灵活的报告机制的需求。 我工作的公司主要专注于开发业务解决方案,而报告是必不可少的,的确,它必须包含我们开发的所有企业系统的方面。 为了在我们的系统中实现灵活的报告,我们已经开发了自己的开源报告生成器-YARG (又一个报告生成器), 该开源文件 (根据Apache 2.0许可分发)。 现在,YARG是CUBA平台报告的核心-CUBA 平台本身就是我们开发的所有系统的基础。
为什么需要开发一个新的
首先,让我指出我们不是车轮发明家。 只要这些解决方案适合我们,我们就一直在寻找与之集成的解决方案。 不幸的是,在这种情况下,我们找不到符合我们确定的以下要求的任何开源工具:
- 以模板格式生成报告和/或将输出转换为PDF
- 避免使用外部工具来创建报告模板(Microsoft Office或Libreoffice应该足够了)
- 支持各种格式的模板: DOC,ODT,XLS,DOCX,XLSX,HTML
- 能够将复杂的XLS和XLSX模板与图表,公式等配合使用。
- 能够使用HTML布局和插入/嵌入图像
- 拆分出数据层(报告结构和数据获取)和表示层(报告模板)
- 启用各种数据获取方法,例如SQL,JPQL或Groovy脚本
- 与IoC框架( Spring , Guice )集成的能力
- 将该工具用作独立应用程序的功能,以便能够在Java生态系统之外使用它(例如,使用PHP生成报告)
- 以透明XML格式存储报告结构
我们可以找到的最接近的工具是JasperReports ,但是有一些阻止程序使我们无法使用它:
- 免费版本无法生成DOC报告(有一个提供此功能的商业库)
- XLS报告非常有限,无法使用图表,公式和单元格格式
- 要创建报告,必须具有一定的技能和知识,以及如何使用非常特定的工具(例如iReports )
- 数据层和表示层之间没有明确的分隔
当然,我们研究了许多其他不同的工具,但是我们发现的所有其他库都集中在某种特定格式上。 我们希望有一个万能的报表功能-一种用于所有报表的工具。
考虑到以上列出的所有观点和想法,我们决定开发另一种定制的报告生成工具。
什么是内幕
当我们启动YARG时,找到用于XLS集成的库( POI-HSSF , JXLS等)不是问题。 我们决定选择Apache POI作为最受欢迎和受支持的库。
DOC集成的情况则完全相反。 开源市场( POI-HWPF , COM和UNO Runtime )只有很少的选择。 POI-HWPF库在许多方面都非常有限,我们不认为它是合适的选择。 我们必须在COM和UNO运行时之间进行选择,这实际上是用于OpenOffice服务器端集成的API。
因此,经过深入调查,我们决定选择UNO Runtime ,主要是因为成功将其用于以完全不同的语言编码的系统(例如Python,Ruby,C#等)的人们的积极反馈。
尽管POI-HSSF的使用非常简单(图表除外),但我们在集成UNO Runtime时面临许多挑战:
- 没有明确的API可用于表格
- 每次生成报告时,OpenOffice都会启动。 最初,我们使用bootstrapconnector来管理OpenOffice进程,但后来很明显,在许多情况下,它不会在生成报告后终止该进程。 因此,我们不得不重新实现OpenOffice启动和关闭的逻辑(感谢jodconverter开发人员,他们在此问题上指出了许多好主意)
- 此外, UNO Runtime (和OpenOffice Server本身)在线程安全方面也存在严重问题,如果发生内部错误,这可能导致服务器冻结或终止自身。 为了克服这个问题,我们必须实现一种机制,以便在服务器发生故障时重新启动报告,这显然会对性能造成不利影响
后来,当DOCX4J库变得非常成熟和流行时,我们支持XLSX / DOCX。 DOCX4J库的主要优点是,它提供了对文档结构的必要的低级访问(基本上,您使用XML进行操作)。 使用DOCX4J的另一个好处是,它不需要OpenOffice服务器集成即可生成DOCX报告。
另外,还可以使用带有Freemarker标记的文档作为报告模板。 我们通常使用它来生成非常自定义的HTML报表,然后将结果转换为PDF 。
最后,YARG基础结构是以可扩展的方式开发的,因此有经验的用户可以自己实现与任何其他模板类型的集成。
你好世界报告
让我们认识一下YARG。 报告生成器的主要思想是将数据层和表示层分开。 数据层使脚本编制或直接SQL查询能够获取所需的信息,而表示层则代表所获取数据的标记。
YARG的所有报告均由所谓的“乐队”组成 。 Band是将数据和表示层链接在一起的东西。 因此,每个乐队都知道从何处获取数据以及将数据放置在模板中的位置。
例如,我们希望将所有员工打印到Excel电子表格中。 我们将需要创建“ Staff”乐队并定义一个SQL查询以获取员工列表:
select name, surname, position from staff
Java代码
ReportBuilder reportBuilder = new ReportBuilder();
ReportTemplateBuilder reportTemplateBuilder = new ReportTemplateBuilder()
.documentPath("/home/haulmont/templates/staff.xls")
.documentName("staff.xls")
.outputType(ReportOutputType.xls)
.readFileFromPath();
reportBuilder.template(reportTemplateBuilder.build());
BandBuilder bandBuilder = new BandBuilder();
ReportBand staff= bandBuilder.name("Staff")
.query("Staff", "select name, surname, position from staff", "sql")
.build();
reportBuilder.band(staff);
Report report = reportBuilder.build();
Reporting reporting = new Reporting();
reporting.setFormatterFactory(new DefaultFormatterFactory());
reporting.setLoaderFactory(
new DefaultLoaderFactory().setSqlDataLoader(new SqlDataLoader(datasource)));
ReportOutputDocument reportOutputDocument = reporting.runReport(
new RunParams(report), new FileOutputStream("/home/haulmont/reports/staff.xls"));
剩下的唯一事情就是创建XLS模板:
开始了! 只需运行该程序即可享受效果!
没有Java的进阶范例
假设我们有一个书店网络。 需要生成一个XLS报告,以显示已售书的列表,并参考售书的书店。 而且,我们没有Java开发人员,只有拥有XML和SQL基本技能的系统管理员。
首先,我们需要为报告创建XLS模板:
如您所见,我们定义了两个命名区域(对应于乐队):商店(蓝色)和书籍实例(白色)。
现在,我们必须从数据库中获取所需的数据:
select shop.id as "id", shop.name as "name", shop.address as "address"
from store shop
select book.author as "author", book.name as "name", book.price as "price", count(*) as "count"
from book book where book.store_id = ${Shop.id}
group by book.author, book.name, book.price
最后,我们使用XML声明报告的波段结构:
<?xml version="1.0" encoding="UTF-8"?>
<report name="report">
<templates>
<template code="DEFAULT" documentName="bookstore.xls" documentPath="./test/sample/bookstore/bookstore.xls" outputType="xls" outputNamePattern="bookstore.xls"/>
</templates>
<rootBand name="Root" orientation="H">
<bands>
<band name="Header" orientation="H"/>
<band name="Shop" orientation="H">
<bands>
<band name="Book" orientation="H">
<queries>
<query name="Book" type="sql">
<script>
select book.author as "author", book.name as "name", book.price as "price", count(*) as "count" from book where book.store_id = ${Shop.id} group by book.author, book.name, book.price
</script>
</query>
</queries>
</band>
</bands>
<queries>
<query name="Shop" type="sql">
<script>
select shop.id as "id", shop.name as "name", shop.address as "address" from store shop
</script>
</query>
</queries>
</band>
</bands>
<queries/>
</rootBand>
</report>
让我们启动报告并查看结果(下面的“ 独立”部分描述了如何运行报告):
该用例表明,您可以引用父带:book.store_id = $ {Shop.id}。 这使我们能够过滤每个特定书店出售的书籍。
一个更高级的例子
现在,我们创建一个发票报告。 我们将创建DOCX文档,然后转换为不可变形式– PDF文档。 为了说明如何加载数据的另一种方式,我们将使用groovy脚本,而不是直接的SQL查询:
<?xml version="1.0" encoding="UTF-8"?>
<report name="report">
<templates>
<template code="DEFAULT" documentName="invoice.docx" documentPath="./test/sample/invoice/invoice.docx" outputType="pdf" outputNamePattern="invoice.pdf"/>
</templates>
<formats>
<format name="Main.date" format="dd/MM/yyyy"/>
<format name="Main.signature" format="${html}"/>
</formats>
<rootBand name="Root" orientation="H">
<bands>
<band name="Main" orientation="H">
<queries>
<query name="Main" type="groovy">
<script>
return [
[
'invoiceNumber':99987,
'client' : 'Google Inc.',
'date' : new Date(),
'addLine1': '1600 Amphitheatre Pkwy',
'addLine2': 'Mountain View, USA',
'addLine3':'CA 94043',
'signature':<![CDATA['<html><body><b><font color="red">Mr. Yarg</font></b></body></html>']]>
]]
</script>
</query>
</queries>
</band>
<band name="Items" orientation="H">
<queries>
<query name="Main" type="groovy">
<script>
return [
['name':'Java Concurrency in practice', 'price' : 15000],
['name':'Clear code', 'price' : 13000],
['name':'Scala in action', 'price' : 12000]
]
</script>
</query>
</queries>
</band>
</bands>
<queries/>
</rootBand>
</report>
您可能已经注意到,Groovy脚本返回List <Map <String,Object >>对象。 因此,每个项目都表示为一个键(参数名称)和值(参数值)。
最后,我们需要创建一个DOCX模板:
要将底部表格链接到书籍列表,我们使用## band = Items标记。
生成报告后,我们得到以下输出:
IoC框架集成
如前所述,其中一项要求是提供集成到IoC框架( Spring , Guice )的能力。 我们将YARG用作CUBA平台(我们用于企业应用程序开发的高级Java框架)中强大的报告引擎的一部分。 CUBA采用Spring作为IoC机制,让我们看一下YARG如何集成到平台中:
<bean id="reporting_lib_Scripting" class="com.haulmont.reports.libintegration.ReportingScriptingImpl"/>
<bean id="reporting_lib_GroovyDataLoader" class="com.haulmont.yarg.loaders.impl.GroovyDataLoader">
<constructor-arg ref="reporting_lib_Scripting"/>
</bean>
<bean id="reporting_lib_SqlDataLoader" class="com.haulmont.yarg.loaders.impl.SqlDataLoader">
<constructor-arg ref="dataSource"/>
</bean>
<bean id="reporting_lib_JpqlDataLoader" class="com.haulmont.reports.libintegration.JpqlDataDataLoader"/>
<bean id="reporting_lib_OfficeIntegration"
class="com.haulmont.reports.libintegration.CubaOfficeIntegration">
<constructor-arg value="${cuba.reporting.openoffice.path?:/}"/>
<constructor-arg>
<list>
<value>8100</value>
<value>8101</value>
<value>8102</value>
<value>8103</value>
</list>
</constructor-arg>
<property name="displayDeviceAvailable">
<value>${cuba.reporting.displayDeviceAvailable?:false}</value>
</property>
<property name="timeoutInSeconds">
<value>${cuba.reporting.openoffice.docFormatterTimeout?:20}</value>
</property>
</bean>
<bean id="reporting_lib_FormatterFactory"
class="com.haulmont.yarg.formatters.factory.DefaultFormatterFactory">
<property name="officeIntegration" ref="reporting_lib_OfficeIntegration"/>
</bean>
<bean id="reporting_lib_LoaderFactory" class="com.haulmont.yarg.loaders.factory.DefaultLoaderFactory">
<property name="dataLoaders">
<map>
<entry key="sql" value-ref="reporting_lib_SqlDataLoader"/>
<entry key="groovy" value-ref="reporting_lib_GroovyDataLoader"/>
<entry key="jpql" value-ref="reporting_lib_JpqlDataLoader"/>
</map>
</property>
</bean>
<bean id="reporting_lib_Reporting" class="com.haulmont.yarg.reporting.Reporting">
<property name="formatterFactory" ref="reporting_lib_FormatterFactory"/>
<property name="loaderFactory" ref="reporting_lib_LoaderFactory"/>
</bean>
为了将YARG集成到Spring Framework中,应注册以下bean:
-
reporting_lib_Reporting
–提供对核心报告生成功能的访问。 report_lib_FormatterFactory –将输出管理为不同的格式(DOCX,XSLX,DOC等) -
reporting_lib_LoaderFactory
–提供数据加载功能(包含与不同源相对应的多个Bean) -
reporting_lib_OfficeIntegration
–将报表生成器与OpenOffice服务器集成(生成DOC和ODT报表所需)
如您所见,YARG可以轻松地嵌入到您的应用程序中。
独立使用
YARG的另一个有价值的功能是它可以用作独立的应用程序。 从技术上讲,如果已安装JRE,则可以从命令提示符下运行报告生成器。 例如,如果您有服务器端PHP应用程序,并且想要在应用程序中启用报告,则只需创建一个XLS模板,以XML声明报告结构,然后从命令提示符下启动YARG:
yarg -rp ~/report.xml -op ~/result.xls “-Pparam1=20/04/2014”
CUBA平台提供的更多功能
YARG已深入集成到CUBA平台中,并充当该平台中实现的强大报告机制的核心引擎。
首先,您可以使用CUBA Studio一键式嵌入报告( 此处提供只读演示版):
CUBA为报表管理提供了方便的用户界面:
- 报告浏览器,带有导入/导出和运行报告的选项。
- 报告编辑器允许您创建任何复杂性的报告(定义带,输入参数,管理模板,使用Groovy,SQL和JPQL选择数据):
- CUBA引入了报告向导功能。 借助向导,即使对编程的了解有限,任何用户都可以快速创建报告:
总结这篇文章,让我跳过通常无聊的深层思考(特别是因为可以在此处找到所有信息),并提出了一些我最喜欢的报告:
因此,如果您有兴趣,请点击链接并了解更多信息! 请注意,YARG是完全免费的,可以在GitHub上使用 。
翻译自: https://www.javacodegeeks.com/2015/09/yet-another-report-generator.html