在实际项目中,特别是企业应用开发中,报表开发是其中很重要的一项功能,基本上都会要求将统计数据导出成Excel,不但如此,这些报表格式一般也比较复杂,尤其是显示的数据,往往都是很多业务数据综合而成的结果。大象根据自己以往做报表的经验,进行了一下总结,给刚开始做报表开发或是有需要的朋友一点借鉴。由于大象以前做的报表距离现在时间已经很久了,有些相关的业务数据已经找不到,再加上这些数据涉及商业机密也不便公开,所以本篇主要着重于阐述解决问题的思路,而不是去关注代码实现,当然代码肯定会有,我主要想告诉大家怎么去解决这类问题。同时大象将在第二篇文章中,把自己遇到过的一些比较复杂的报表案例拿出来进行分析,用抽丝剥茧的方式,让报表开发变得不再那么让人无处下手,最多只是麻烦点而已。^_^
目前Java开源报表比较有代表性的有JasperReports、Cewolf、iReport、JfreeChart等等技术,它们大都是用来生成不同格式或图形化的报表。而本篇文章要讲的jxl,则是Java Excel API,它是一个成熟开源的Excel电子表格读取,修改,写入的项目。我们可以利用它简单,便利的API生成我们想要的Excel数据报表。除了jxl外,还有一个apache的poi开源项目,它的功能非常强大,不仅可以操作Excel,还可以支持Word、PowerPoint等等格式,3.5之后的版本,还加入了对Excel 2007以后版本的支持。
当然jxl也有不足的地方,最主要体现在,它支持生成的报表最大行数为65535,如果超过这个数量,将会产生异常,而且也不支持Excel 2007+版本。如果平时报表数据量不是很大的话,而且只是用来生成Excel格式的文件,大象建议还是用jxl比较好,它使用简单,性能也还不错。
根据大象的经验,定义一个报表模板,然后往里面填充数据,是开发复杂报表的一个比较不错的方法。大象将报表分为基本报表和复杂报表,所谓基本报表就是没有复合表头,每行一条数据,并且提供的数据不需要查询两张以上的表。而复杂报表正好与之相反。不管是基本报表还是复杂报表,代码有很多都是一样的,不一样的只是由业务复杂度产生的报表复杂度。
我们先来看看用jxl生成Excel报表的代码模板:
上面这个是Cell(单元格)的通用格式化设置,WritableFont构造函数的参数第1个是字体,第2个是字体大小,第3个是字体样式,第4个false指的是正常显示,默认为italic倾斜显示,第5个下划线样式,第6个指定字体颜色。设置完字体后,再设置单元格的格式,对于一般的单元格,不用设置背景色,除非报表显示需要。金额及百分比的显示要用到NumberFormat类,这里的格式指定为0.00而不是#.##是为了强制保留两位小数,对于这类数字一般在报表中都是设置为右对齐,这样出来的报表比较好看。当然最终还是要根据客户的要求来决定样式。
使用模板方式生成Excel除了已经定义好表头外,还有一个好处是可以预先定义固定列的宽度,这样就免去了用代码来设置列宽,如果是复杂表头,也省去了不少设置表头和合并单元格的代码。请注意我前面用红字加粗的固定列三个字,因为报表的表头不一定就是模板中设置的那样,还有一种情况就是根据查询条件的年和月,动态生成表头,这些表头被排列在固定列的后面,报表生成后,这些列下面都有相应的数据。关于这部分内容我会在第二篇文章中介绍。上图中,红框的部分就是使用模板生成Excel的关键代码,ResourceUtils是org.springframework.util包中的工具类。
上图中的红框部分与使用模板方式相比是不是点些细微的差别?请一定注意这些细节部分的不同。完全用jxl API创建Excel所有内容都需要用代码来实现:
设置标题及表头并定义每列的宽度,setColumnView方法的第1个参数是列的索引,从0开始,第2个参数是宽度,这个数值既不是像素也不是长度,而是指字符数,比如第1列的宽度可以理解为相当于20个字符的宽度。以10pt的字体大小来算,实际上最多只能填充19个字符,而汉字最多10个,其实主要还是需要通过测试来调整最终效果。
接下来就是填充数据,这块代码不管是用模板方式还是API方式都是一样的。
对于简单的单行表头,填充数据很简单,将查询结果填充到单元格中。前面定义了标题和表头,已经占去了两行,所以应该从第3行开始添加数据,行的索引也是从0开始,2表示第3行。如果是数字型的Cell需要用jxl.write.Number来进行封装,这样生成的数据才不会因为可能过大或别的因素而被Excel自动转化成科学计数法。如果还是想用Lable来封装数字,可以使用java.text. NumberFormat来做格式化。最后执行关闭操作。
看到这里,有没有感觉用jxl做报表非常简单?可以说是,也可以说不是。因为这里面最麻烦的地方在于取得业务数据,另外对于复杂报表,for循环里面还会包含很多条件判断、其它for循环以及合并行或列等操作。业务数据的获得有两种方式,第1种是按报表的样式建立一个对象模型,将要导出的数据全部填充到这个模型中,然后根据报表的格式添加数据。第2种是通过SQL语句,使用尽量少的查询次数将所有结果查询出来,填充到数据模型中。不管是第1种还是第2种最典型的就是简单表头,单表查询,所有的数据都在Model对象里面,执行一个循环操作就可以搞定了。但如果是复杂报表,第1种做法就不可取了,因为当你将这个数据对象的List实现后就会发现,代码之复杂,效率之低下(并不是指速度慢,而是指SQL查询次数非常多)简直令人发指。而如果采取第2种做法,SQL语句无疑会很复杂(不考虑视图),会有很多关联查询,如果作了分表分库的设计,那这个方式就彻底完蛋了。既然这两种都不是好的解决办法,那么究竟还有没有更好的解决方案呢?肯定是有的,就是分而治之,按照一种规则(比如时间),提取出与报表相关的最基础数据至Table1中,然后对该Table1再进行一次处理合并至Table2中,提取Table2至Table3,如果有必要还可以对Table3再处理一次到Table4,从Table1到Table4每个表里面都是一种报表数据,如果按时间分组,假定Table1是按小时,Table2按天,Table3按月,Table4按年,那么相应的报表查询就可以做到只对一张表进行操作,而且报表需要的业务数据都已经存在该表中。我所举的这个方法是比业务较简单的情况,实际开发中要根据当前的业务来设计,因为有些项目的业务确实是非常的复杂,并不像我所说的这么简单。大家一定不要过于迷信这种方式,世上是没有完美解决方案的,也即所谓的没有“银弹”,这样的处理方式对大部分报表还是适用的。但如果要是遇到所统计的业务基础数据发生了变化,或者本身业务出现变更,甚至报表都发生变更,那么之前所获得的这些数据就没有什么意义了。因此,报表的实现方案一定要根据实现情况来分析,不要总想着有什么通用的解决办法,一招鲜吃遍天这样的想法是非常不可取的,软件最不缺的就是变化,不要抗拒变化,因为你根本无法阻止它,所以你得拥抱变化,享受变化。
本篇主要介绍了使用jxl生成报表的代码模板以及获取报表数据的一种处理方式,下一篇将通过两个复杂报表案例的分析,来告诉大家如何进行实现。本文为菠萝大象原创,如要转载请注明出处。
posted on 2014-01-07 16:12 菠萝大象 阅读(4719) 评论(2) 编辑 收藏 所属分类: Java