最近在整理项目上的报表功能,因为项目比较久远,留存的文档也少,因此重新部署过程中也遇到了不少的坑,记录下来方便后来者。项目中用的是Birt来自定义报表,当时选择Birt的原因主要是因为产品的需要,为了满足用户个性化报表的需求,而且以最小的开发量来快速的定制化报表,预览报表以及生成报表文件等功能。但由于Birt也是一个比较老的技术,熟悉.net的朋友应该都有过水晶报表的开发经验,Birt很类似于水晶报表。Birt现在的更新停留在2016年,因此这几年也没有任何的更新,会存在一些问题:
- JDK版本的支持,JDK的版本目前测试只能到1.8,而且其他类型的JDK,比如corretto JDK 1.8有些功能不能正常运行
- 由于很多库的版本比较低,一些涉及到安全的库升级比较困难。
- 另外它是基于eclipse插件开发,因此暂时在IntellJ上还不支持。
- 当前的很多实例都是基于JAVA EE开发,新的SpringBoot实例比较少。因此,本文在总计Birt的基本使用的基础上,将使用一个实际的实例来详细演示如何在SpringBoot上集成Birt,并提供报表的在线和离线生成功能。
Birt介绍BIRT是一个Eclipse-based开放源代码的报表系统,它主要是用在基于Java和J2ee的web应用程序上。BIRT主要由两部分组成:一个是基于Eclipse的报表设计器和一个可以添加到应用服务器的运行组建。BIRT同时提供一个图形报表制作引擎。BIRT可以生成图片、导出Excel、pdf等。
基本概念1)数据源,数据的提供者。如xml数据源、jdbc数据源等。也支持Hive和Cassandra等大数据源。也可以支持脚本数据源。2)数据集,数据集合,即查询的结果。3)报表以及报表项,报表可视为是一组数据集的表现形式,而报表项是具体形式表现的单元。4)报表参数,用于查询报表的查询参数5)模板和库,主要用于复用报表,提高报表开发的效率。
报表类型列表
列表是最简单的报表。当列表变长时,你可以把相关数据增加到同一分组。如果数据是数字类型的,你可以添加到“总数”、“平均”、或其他汇总中。
图表
当需要图表表现时,数字型数据比较好理解。BIRT 也提供饼状、线状以及柱状图标等。
交叉表
交叉表(也叫做十字表格或矩阵)用两种维度展示数据。
信函和文档
通知、信件、以及其他文本文档都很容易通过 BIRT 方便建立。文档包括正文、格式、列表、图表等。
混合报表
可以组合各种类型的报表到一个报表上。
报表设计器
包括布局视图(Layout),属性编辑器(Property Editor),报表预览(Preview),代码编辑器(Script)等。整体。如下图:
准备环境下载download birt开发工具 http://download.eclipse.org/birt/downloads 选All in one,安装后打开eclipse.exe或者自行下载eclipe安装,然后安装Birt runtime和design tool。
报表设计第一步,创建报表
File->new->New Report , 新建立一个report,命名为book.rptdesign,放在resouces/report目录下
第二步,创建数据源
在Outline里找到Data source,新建DataSource,在下拉框里选Scripted Data Source,Scripted Data Source是指通过script来获取到数据源。
第三步,创建dataset
在Outline里找到刚刚建立Data set,双击找到DataSource,关联上刚刚建立的DataSource。Data set 是指从DataSource来组装出需要的数据集合。
第四步,设置dataset输出列
在Outline里找到Data set,新建DataSet,在OutPut columns填入Year type选string,name type选String,author type选String。
第五步,设置报表参数
在Outline里找到Report Parameters,新建立一个参数为year,Data type为String,Display type为Text Box。
第六步,设置body
在Outline里找到Body,在Outline里找到Rport Items里,拖动一个Table到右侧的layout里,会自动弹出对话框,DataSet选刚才的建立的DataSet,选中binding columns。并输入表头并绑定cell到dataset的输出参数。
第七步,预览报表
预览report,可以在birt里选window->preference->web browser,勾上use external web browser ,在下面的选项框里选你的浏览器。然后在工具栏里选择播放标志的下拉选view report as html或其它的选项。预览时,会提示输入年份,输入年份后,就可以看到报表的雏形。
SpringBoot服务开发使用SpringBoot开发后端服务,来为报表数据提供数据源。下面以一个简单的实例来演示如果生成报表。
引入依赖包
如果需要预览报表,需要引入更多的包,axis,commons-discovery,
viewservlets,jaxrpc-api
BirtEngineFactory
public class BirtEngineFactory implements FactoryBean
config.getAppContext().put("spring", this.context );在报表里就可以使用下面的语句来获取ApplicationContext,进而获取bean实例。
spring = reportContext.getAppContext().get("spring");var bookService = spring.getBean("bookService");BirtConfiguration
@Configurationpublic class BirtConfiguration { @Value("${birt.log.location}") String logLocation; //产生FactoryBean
@RestControllerpublic class ReportController { @Autowired BirtReportGenerator birtReportGenerator; Logger logger = LoggerFactory.getLogger(ReportController.class); @PostMapping("/report/book/{searchBy}/{seachValue}") public String searchCarReport(@PathVariable("searchBy") String searchBy, @PathVariable("seachValue") String searchValue) { ReportParameter rm = new ReportParameter("book", "PDF"); rm.setParameter(searchBy, searchValue); try { ByteArrayOutputStream baos = birtReportGenerator.generate(rm); FileOutputStream fops = new FileOutputStream("c:/report/bookreport_" + System.currentTimeMillis() + ".pdf"); fops.write(baos.toByteArray()); fops.close(); baos.close(); } catch (Exception e) { logger.error("Error: " + e.getMessage()); } return "success"; }}BirtGenerator
@Componentpublic class BirtReportGenerator { @Autowired private IReportEngine birtEngine; public ByteArrayOutputStream generate(ReportParameter rptParam) throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(); IReportRunnable runnable = null; ClassPathResource cpr = new ClassPathResource("report/" + rptParam.getReportName() + ".rptdesign"); runnable = birtEngine.openReportDesign(cpr.getInputStream()); IRunAndRenderTask runAndRenderTask = birtEngine.createRunAndRenderTask(runnable); runAndRenderTask.setParameterValues(setParameters(runnable, rptParam.getParameter())); IRenderOption options = new RenderOption(); if (rptParam.getFormat().equalsIgnoreCase("pdf")) { PDFRenderOption pdfOptions = new PDFRenderOption(options); pdfOptions.setOutputFormat("pdf"); pdfOptions.setOption(IPDFRenderOption.PAGE_OVERFLOW, IPDFRenderOption.FIT_TO_PAGE_SIZE); pdfOptions.setOutputStream(baos); runAndRenderTask.setRenderOption(pdfOptions); } runAndRenderTask.getAppContext().put(EngineConstants.APPCONTEXT_CLASSLOADER_KEY, this.getClass().getClassLoader()); runAndRenderTask.run(); runAndRenderTask.close(); return baos; } protected HashMap
@Servicepublic class BookService { static List
initialize脚本
在Birt里打开book.rptdesign,在Outline里找到Scripts,点book.rptdesign,然后在右边的窗口里切换到script,在左上方的script下来里找达到initialize,然后输入以下代码
spring = reportContext.getAppContext().get("spring");var bookService = spring.getBean("bookService");open脚本
在Birt里打开book.rptdesign,在Outline里找到dataset,然后在右边的窗口里切换到script,在左上的script下来里找到open,然后输入以下代码
var bookService = spring.getBean("bookService");listdata = bookService.getBooksByYear(params["year"]);count = 0;fetch脚本
在Birt里打开book.rptdesign,在Outline里找到dataset,然后在右边的窗口里切换到script,在左上的script下来里找到fetch,然后输入以下代码
if(listdata.size() > count){ book = listdata.get(count); row.year = book.getYear(); row.name = book.getName(); row.author = book.getAuthor(); count++; return true;}return false;运行报表eclipse运行此报表程序,或者使用java -jar来运行此报表程序。打开postman,选择post请求,输入http://127.0.0.1:8080/report/book/year/2005,在c:/report下面将生成一个pdf文件。
报表预览报表预览需要在SpringBoot中引入Java Web环境,并拷贝一些资源文件和模板,配置相应的属性才能实现在web页面上浏览报表,详细可以参考文章
https://www.ibm.com/developerworks/library/ba-birt-viewer-java-webapps/index.html#list3
常见问题
在开发过程中,在eclipse中经常修改后不能生效,应该是eclipse的问题,在eclipse中clean也不能生效,解决办法是在命令行中运行mvn clean package来生成包。
要理解脚本的执行顺序,为了提高效率,可以使用变量来缓存一些数据,比如在报表的脚本中需要调用后端的服务来获取数据,最好的办法是获取一次并存到global variable中,后续的脚本可以直接从reportContext中取获取这个全局变量来提高报表的渲染效率。使用方式如下:
reportContext.setPersistentGlobalVariable("变量名", 变量值);reportContext.getPersistentGlobalVariable("变量名");
如果需要调试报表脚本,可以引入日志功能,简单输出日志的方法如下:
importPackage(Packages.java.lang);System.out.println("你的日志信息");