最近接了一个报表的任务,原来也有接触过,但是仅限于了解没有真正的动手画过。这里把从选型到最后成型一路下来遇到的问题整理下。
主要两个大问题:环境的搭建,以及子报表(嵌套报表)的配置。
选择Jasperreports
知道的有Birts、Pentaho、FineReport感觉其实都差不多,大家各自都取长补当更多。由于一穷二白的,没有弄过。网上找了和SpringMVC结合的都是Jasperreport的文章,就这么草率的定下来了。
基本的操作都类似,报表HelloWorld还是比较简单的。下载jaspersoftstudio最新版,然后了解各个区域的作用。
一个完全的报表模板包括如下几个区域:title, pageHeader, columnHeader, groupHeader, detail, groupFooter, columnFoter, pageFooter, summary
整合SpringMVC
原来做报表的同时都是直接连数据库的,过程中遇到各种问题。很多没法维护的事情发生:改个字段,数据库测试/生产链接等等。我这里直接选择通过JavaBean来传递数据。
贴代码之前先说PDF报表字体的问题,本来报表是加粗的,但是服务器生成浏览的时刻没有效果。发现还需要单独添加字体的包:pdf - Bold not working in Jaspersoft Studio for fonts other than sans serif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
| #maven
<properties>
<spring.version>4.2.5.RELEASE</spring.version>
...
<dependency>
<groupId>net.sf.jasperreports</groupId>
<artifactId>jasperreports</artifactId>
<version>6.3.1</version>
</dependency>
<dependency>
<groupId>net.sf.jasperreports</groupId>
<artifactId>jasperreports-fonts</artifactId>
<version>6.0.0</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itextpdf</artifactId>
<version>5.5.10</version>
</dependency>
<dependency>
<groupId>com.itextpdf</groupId>
<artifactId>itext-asian</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy-all</artifactId>
<version>2.4.7</version>
</dependency>
# spring mvc xml
<bean id="viewResolver" class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
<property name="order" value="0"></property>
<property name="basename" value="views"></property>
</bean>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="order" value="1"></property>
<property name="viewClass">
<value>org.springframework.web.servlet.view.JstlView</value>
</property>
...
# views.properties
HELLO.(class)=org.springframework.web.servlet.view.jasperreports.JasperReportsPdfView
HELLO.url=/WEB-INF/report/helloworld.jasper
HELLO.reportDataKey=datasource
# java Controller
@Controller
@RequestMapping("/report")
public class HelloReportController {
@RequestMapping("/hello.pdf")
public ModelAndView printExpress() {
ModelAndView mv = new ModelAndView("HELLO");
// 如果直接传对象bean不行,需要使用list传值
List<HelloWorldData> list = new ArrayList<>();
list.add(new HelloWorldData("jarperreport", "Hi"));
mv.addObject("datasource", list);
return mv;
}
|
最后通过浏览器就能查看报表的PDF文件了。
前端所有的页面都是通过ajax来获取展示的,这里通过jquery-media.js来进行展示(生成内嵌的iframe),这也是上面的地址加上pdf后缀的原因。
1
2
3
4
5
6
7
| # html
<div class="modal-body" style="max-height: 900px; padding: 10px;">
<a class="media" href="${contextPath}${url}"></a>
</div>
# jquery
$('a.media', $modal).media({width:"100%", height:600});
|
子报表
有一个结账的报表,既要展示汇总信息,还得把详情列表也输出出来。一开始的JavaBean:
1
2
3
4
5
6
7
8
9
10
| public class InvoiceData {
private String roomNo;
private List<InvoiceDetailData> details;
...
}
public class InvoiceDetailData {
private String date;
private String amount;
...
}
|
但是简单报表是一维的,不能展示list里面的内容。网上一堆资料都是简单的案例,涉及多维度的就Table、Crosstable、Subreport这几个控件。Table的样式调起来麻烦,数据也不知道怎么搞。子报表至少看起来合乎逻辑,操作起来也简单。画好图标以及把对应的字段对应好后,子报表的Datasource直接填 $F{details}
。
修改views.properties,写好controller后,启动竟然报找不到details子报表路径。根据文章修改如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| # 主报表
# 类型必须加哦!
<parameter name="DetailSubReport" class="net.sf.jasperreports.engine.JasperReport"/>
...
<subreport>
<reportElement stretchType="RelativeToBandHeight" x="0" y="0" width="520" height="167" uuid="8d69d85b-4fcf-482a-836c-c1698ce42dcd"/>
<dataSourceExpression><![CDATA[$F{details}]]></dataSourceExpression>
<subreportExpression><![CDATA[$P{DetailSubReport}]]></subreportExpression>
</subreport>
# views.properties
Invoice.(class)=org.springframework.web.servlet.view.jasperreports.JasperReportsPdfView
Invoice.url=/WEB-INF/report/invoice.jasper
Invoice.reportDataKey=datasource
Invoice.subReportUrls=DetailSubReport=/WEB-INF/report/InvoiceDetail.jasper
|
罗马建成非一日之功,再次编译启动后,再次报错,这次的是类型错误。感觉正在慢慢向成功靠近。修改类型后最后启动展示搞定。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| # javabean
public class InvoiceData {
private JRDataSource details;
public void setDetails(JRDataSource details) {
this.details = details;
}
public void setDetails(List<InvoiceDetailData> details) {
this.details = new JRBeanCollectionDataSource(details);
}
# 主报表
<field name="details" class="net.sf.jasperreports.engine.JRDataSource">
<fieldDescription><![CDATA[details]]></fieldDescription>
</field>
|
–END