java学习之JasperReport踩坑

java学习之JasperReport踩坑
下⾯进⼊正题,来介绍下今天的猪脚JasperReport或者叫它ireport亦或jasperstudio,当然后⾯两个是它的可视化⼯具。
JasperReport是个什么东西?
这货其实在国内⽤户也不少,是个国外的产品,⽽且可以说在JAVA报表领域应⽤是相当的⼴泛。
我当初刚刚接触这个报表的时候还是相当的喜欢的,最主要的是它的可视化⼯具,真的是让我欲罢不能,竟然可以通过简单画图的⽅式来设计JAVA报表。说起画图就是可以通过可视化的⼯具,让我们可视化的设计报表模板,并且它⽀持输出的⽂件格式很⼴泛,包括EXCEL、WORD、PDF、HTML、XML、CSV等等。
看起来是不是很强⼤,⼀次设计,多次复⽤。当然强⼤得的东西,往往都有两⾯性,这不就被我遇到了,折磨了我相当长的时间,后⽂会详细描述的。
JasperReport的⼤胸弟
前⾯我说,JasperReport或者叫它ireport或jasperstudio,其实这是不准确的。⼆弟ireport、三弟jasperstudio其实是jasper的辅助视觉设计⼯具,你不⽤它也能设计jasper报表,多写点XML⽩。
5.5之前这个⼯具叫ireport,5.5之后随着三弟jasperstudio的出⽣,ireport就被完全替代了,其实这两个⼯具基本上是⼀样的,⼀奶同胞。
具体的⼯作流程:
①⾸先Jasper会获取需要输出的格式信息的xml⽂件,然后从xml⽂件中编译出.jasper类型的⽂件,然后这个jasper⽂件可以在我们的应⽤程序中被加载⽣成最终的报表。有没有很熟悉的感觉,是的,这⼀点和java很像,都需要编译⼀下。
下图,就是ireport的操作界⾯,jasperstudio类似,就不贴了,⼤家可以⾃⾏百度下。
上图每种类型的band简单介绍⼀下。
(1)Title band:title段只在整个报表的第⼀页的最上⾯部分显⽰,除了第⼀页以外,不管报表中共有多少个页⾯也不会再出现Title band中的内容。
(2)pageHeader Band:顾名思义,pageHeader 段中的内容将会在整个报表中的每⼀个页⾯中都会出现,显⽰在位置在页⾯的上部,如果是报表的第⼀页,pageHeader 中的内容将显⽰在Title Band下⾯,除了第⼀页以外的其他所有页⾯中pageHeader中的内容将在显⽰在页⾯的最上端。
(3)pageFooter Band:显⽰在所在页⾯的最下端。
(4)lastPageFooter Band:显⽰在最后⼀页的最下端。
(5)Detail Band:报表内容段,在这个Band 中设计报表中需要重复出现的内容,Detail 段中的内容每页都会出现。
(6)columnHeader Band:针对Detail Band的表头段,⼀般情况下在这个段中画报表的表头。
(7)columnFooter Band:针对Detail Band的表尾段。
(8)Summary Band:表格的合计段,出现在整个报表的最后⼀页中的Detail band 的后⾯,⼀般⽤来统计报表中某⼀个或某⼏个字段的合计值。
上⾯就是可视化的⼯具的全部,其实怎么⽤很简单,上⼿摸索下就会了,既然是踩坑实录,这个⾃然不是重点,不说了。
代码中的应⽤
这是我总结的步骤,可能描述的不是很准确,⼤家凑合下
①设计模板,⽣成JRXML⽂件,↑↑上⾯的可视化⼯具设计你所需要的模板样式
哈卡斯人②编译模板,JRXML编译成Jasper⽂件,就像java中的.java和.class⽂件⼀样,程序中运⾏的需要是*.jasper的⼆进制⽂件。
其实这⼀步可以直接⽤ireport编译⽣成.jasper,当然也可以在运⾏时通过jasper程序编译。但是建议如果在程序中编译的话,jasper版本最好和ireport或者jasperstudio的版本⼀致。
③执⾏报表(数据填充到报表)
1、加载模板⽣成Jasperreport对象
2、利⽤JasperFillManager,⽣成JasperPrint对象
mgmb④最后利⽤JRXlsxExporter导出类,将报表导出或者展⽰
加载模板
既然我们已经利⽤可视化⼯具⽣成了.jasper或者.jrxml⽂件了,⾃然是需要让程序加载它。
加载的代码,返回jasperport对象
if (dsWith(".jrxml")) {
//compile jrxml to jasper
try {
InputStream is = url.openStream();
jasperReport = JasperCompileManagerpileReport(is);
} catch (IOException e) {
throw new BaseException("Load jasper error", e);
} catch (JRException e) {
throw new BaseException("The jrxml template transform to jasper file error", e);
} catch (Throwable e) {
<(e);
throw new Message());
}
} else if (dsWith(".jasper")) {
try {
InputStream is = url.openStream();
jasperReport = (JasperReport) JRLoader.loadObject(is);
} catch (IOException e) {
throw new BaseException("Load jasper error", e);
} catch (JRException e) {
throw new BaseException("The jrxml template file error", e);
} catch (Throwable e) {
<(e);
throw new Message());
}
} else {
throw new BaseException("Invalid file!");
}
获取报表中的数据源
这⾥我采⽤javabean的⽅式获取
      JRDataSource dataSource = null;
惯习if (fieldValues != null && fieldValues.size() > 0) {
dataSource = new JRBeanCollectionDataSource(fieldValues);
} else {
dataSource = new JREmptyDataSource();
}
fieldValues 为数据库中获取的pojo集合。
执⾏报表填充
得到jasperprint对象
Map<String, Object> parameterValue = new HashMap<String, Object>();
jasperPrint = JasperFillManager.fillReport(jasperReport, parameterValue, dataSource);
最后我们利⽤JRXlsxExporter导出报表
这个也是需要配置参数最多的⼀个地⽅
baos = new ByteArrayOutputStream();
exporter = new JRXlsxExporter();
exporter.setParameter(JRExporterParameter.JASPER_PRINT, jasperPrint);
exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, baos);
完成,数据已经写⼊输出流中了,怎么输出⾃⼰决定,是不是⽐其他⽅式代码简介很多。
确实在代码书写中JasperReport有着⽆法⽐拟的优势,各种api已经封装好。但是可能是恰恰做的太多,问题也不少。
JasperReport的问题
1、两⾏前的空⽩
如果你使⽤上⾯的代码导出EXCEL的话,你会发现Excel的背景是⽩⾊,没了Excel⼀个个的⼩格⼦,这是因为jasper默认背景为⽩⾊,这样在导出其他格式时也好做到兼容,当然当我们导出EXCEL并不需要。只需要加上下⾯两⾏就可以解决。
//去除两⾏之前的空⽩
exporter.setParameter(JRXlsExporterParameter.IS_REMOVE_EMPTY_SPACE_BETWEEN_ROWS,Boolean.TRUE);
exporter.setParameter(JRXlsExporterParameter.IS_REMOVE_EMPTY_SPACE_BETWEEN_COLUMNS,Boolean.TRUE);
//设置Excel表格的背景颜⾊为默认的⽩⾊
exporter.setParameter(JRXlsExporterParameter.IS_WHITE_PAGE_BACKGROUND,Boolean.FALSE);
2、数据量很⼤,title多次写⼊
如果你⼀个Sheet数据很多,可能会遇到表头多次打印的情况,这种情况下,你需要加上⾼度设置。
Field pageHeight = DeclaredField(
"pageHeight");
pageHeight.setAccessible(true);
pageHeight.setInt(jasperReport, Integer.MAX_VALUE);
3、Cell的类型的问题
有时候我们导出的Excel报表,需要使⽤Excel的函数计算,如果全都是⽂本格式,⾃然计算不了,这种情况下,我们需要使⽤
//⾃动选择格式
exporter.setParameter(JRXlsExporterParameter.IS_DETECT_CELL_TYPE, Boolean.TRUE);
切记,在报表设计时,Field字段选择正确的类型。
4、多Sheet的问题
我上⾯那个简单的例⼦,只是⼀个⽂件中包含⼀个Sheet页,假如我们的需求是⼀个⽂件导出多个Sheet怎么办,别急,这个Japser早已为我们想到了。
只需要将上⽂中导出步骤换成下⾯这个样⼦
baos = new ByteArrayOutputStream();
exporter = new JRXlsxExporter();
exporter.setParameter(JRExporterParameter.JASPER_PRINT_LIST, listJasperPrint);
exporter.setParameter(JRExporterParameter.OUTPUT_STREAM, baos);
//设置为true,即可在⼀个excel中,每个单独的jasper对象放⼊到⼀个sheet页中
exporter.setParameter(JRXlsExporterParameter.IS_ONE_PAGE_PER_SHEET,Boolean.TRUE);
JRExporterParameter.JASPER_PRINT_LIST,传⼊⼀个listJasperPrint的集合,每个JasperPrint即⼀个Sheet页。
5、Linux下启动不报错,但是⽆法导出报表
其实这个问题也困扰了我很久,后来在⼤佬的帮助下才想起来问题所在,因为它抛出的根本不是个Exception,⽽是Error。我看到⽹上也有同学问这个问题,所以贴出来。
可以⽤throwable捕获,就可以得到错误信息,报错:java.lang.InternalError: Can't connect to X11 window server using ':0.0' as
解决⽅法:修改tomcat/bin/catalina.sh 加JAVA_OPTS="$JAVA_OPTS  -Djava.awt.headless=true"
6、⼤数据内存溢出和内存泄露问题!!
这⾥需要说⼀下,EXCEL 03和07版的区别,03版我记得好像是只⽀持65532⾏吧,⽽07版之后就⼤的多了,具体数字我忘了,反正不是⼀个数量级的。
JRXlsxExporter⽀持导出xlsx⽂件,
JRXlsExporter则是xls的⽂件,很好辨认,导出的⼯具和excel的格式⼀样。
然后是内存溢出和内存泄露问题,这个我相信玩JAVA的朋友基本上都遇到过。
关于内存溢出最通常的解决办法便是增⼤容器的内存,增加tomcat的内存⼤⼩,⽅法⼤家可以百度,有很多,不重复造轮⼦了。
这⾥提醒下,如果你使⽤的是tomcat的话,windows安装版,解压缩版和Linux版的配置⽅式都是不同的,需要注意下。
这⾥我需要介绍的是JasperReport的⽅式,其实JasperReport是对⼤数据有解决⽅案的,在很早期的版本便推出了,JRFileVirtualizer的仿真器。
这个东西是做啥⽤的呢,其实它会根据你设置的参数,将数据写到硬盘的临时⽂件上,这样解决了填充报表时内存占⽤过⼤溢出的问题。
⽬前JasperReport有3个仿真器,都是⽤来解决这个问题的。
分别是:
①JRFileVirtualizer
②JRSwapFileVirtualizer
③JRGzipVirtualizer
这三个仿真器⼜有什么区别呢?
⾸先是推出最早的JRFileVirtualizer,我在测试时,当导出30W左右的数据,就会报内存溢出,后来加上这个后就可以正常导出了。这个仿真器会把每⼀个对象⽣成⼀个临时⽂件存放在硬盘上解决内存占⽤的问题,但是因为产⽣的临时⽂件较多,⽆形中增加了⽂件创建和删除的内存消耗,所以并不是很推荐。
//写多个⽂件
应用集成JRFileVirtualizer virtualizer = new JRFileVirtualizer(2, catchPath);
Map<String, Object> parameterValue = new HashMap<String, Object>();
parameterValue.put(JRParameter.REPORT_VIRTUALIZER, virtualizer);
virtualizer.setReadOnly(true);
catchPath为⽂件缓存路径,必须真实存在,否则会报错。
硅酸盐学报然后是JRSwapFileVirtualizer,这个是为了解决JRFileVirtualizer的问题⽽推出的。这个仿真器,只会创建⼀个临时⽂件,每个对象会占这个⽂件的⼀部分,所以就减少的⽂件创建和删除的内存消耗,其实这个也不是特别推荐。
//写单个⽂件
RSwapFile arquivoSwap = new JRSwapFile(catchPath, 4096, 25);
JRAbstractLRUVirtualizer virtualizer = new JRSwapFileVirtualizer(2, arquivoSwap, true);
Map<String, Object> parameterValue = new HashMap<String, Object>();
parameterValue.put(JRParameter.REPORT_VIRTUALIZER, virtualizer);
virtualizer.setReadOnly(true);
最后是JRGzipVirtualizer这个,看到Gzip,不知道你是否有联系到压缩这个词汇。没错,这个仿真器就是使⽤⼀种特殊的压缩算法,可以将内存占⽤压缩到⼆⼗分之⼀还是⼗分之⼀来着,总之很神奇。
JRAbstractLRUVirtualizer virtualizer = new JRGzipVirtualizer(2);
Map<String, Object> parameterValue = new HashMap<String, Object>();
parameterValue.put(JRParameter.REPORT_VIRTUALIZER, virtualizer);
jasperPrint = JasperFillManager.fillReport(jasperReport, parameterValue, dataSource);
说了这么多,总之就是三种仿真器解决内存溢出问题,我也看了很多博客⾥⾯写利⽤JRFileVirtualizer,解决内存⼤数据问题。然后我在这⾥想说,我最最最不推荐使⽤JRFileVirtualizer仿真器,因为它不仅创建⽂件消耗⼤,还有个很严重的BUG,内存泄露还有JRSwapFileVirtualizer也有这个问题。
另外,需要说明的是不使⽤仿真器,也会有内存泄露的问题,当你导出报表后,dump出堆栈信息,会发现net.ine.fill.JRTemplatePrintText类的实例特别多,⽆法回收,⽆法回收并且最新版的japserreport 6.x依旧存在这个问题,在jasper的社区和Stack Overflow存在很多这样的问题,⽽没有解决⽅案。血清肌酸激酶
这⾥推荐JRGzipVirtualizer仿真器,虽然依旧存在泄露问题,但是因为独特的压缩算法,已经将内存泄露问题控制在很⼩的范围⾥了,算是⼀种缓解的⽅案吧,⼤概泄露的内存占⽤缓解了九成以上。
总的来说,我现在已经放弃这种⽅案了,写出来也是为了后来的兄弟少⾛弯路。撸了⼀个POI的⼯具类,接下来准备把所有的报表改成POI导出的⽅式,话说POI的⼤数据⽅案还是挺不错的。

本文发布于:2024-09-23 03:22:22,感谢您对本站的认可!

本文链接:https://www.17tex.com/xueshu/292714.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:报表   内存   问题   需要
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议