6.8 在Struts中使用JasperReports
JasperReports(简称JR)是目前应用广泛并且技术领先的Java开源报表库。它将.jrxml(XML源文件)编译为.jasper(编译后版本)文件,它可以被转换为多种格式输出(PDF、CSV、XLS和HTML)。本节以一个例子讲述JasperReports在Struts2中是如何配置和使用的。关于JasperReports的原理和详细使用方法请参考JasperReports自身文档。
6.8.1 准备JasperReports库
在使用JasperReports之前,需要将JR库添加到classpath。可以从“http://www.sourceforge.net/projects/jasperreports”下载JR项目。将jasperreports-X-project.zip存储到硬盘,将文件解压缩。需要如下文件:
❑ dist/jasperreports-X.jar
❑ lib/commons-*.jar (all the commons-except maybe for commons-logging)
❑ lib/itext-X.jar
❑ lib/jdt-compiler.jar
将这些jar拷贝到/WEB-INF/lib目录,然后将它们添加到CLASSPATH。
JasperReports在Struts2中是作为一个plugin出现的,需要在Struts2包提供的lib目录中找到struts2-jasperreports-plugin-xxx.jar,把这个jar文件也添加到/WEB-INF/lib目录,同样添加到CLASSPATH。
6.8.2 定义值对象
先定义一个简单的POJO(简单的Java对象,Plain Old Java Objects),实际就是普通JavaBeans。使用POJO名称是为了和EJB区分,而且简称比较直接。有一些属性及其getter、setter方法的类,有时可以作为值对象来使用,如实例6-29所示。
【实例6-29】关于对象类:Person.java
01 /* 02 *简单POJO不包含任何复杂逻辑 03 */ 04 public class Person { 05 //创建各种变量 06 private Long id; //创建id变量 07 private String name; //创建name变量 08 private String lastName; //创建lastName 变量 09 //无参数构造函数 10 public Person() { 11 } 12 //带参构造函数 13 public Person(String name, String lastName) { 14 this.name = name; 15 this.lastName = lastName; 16 } 17 //带参构造函数 18 public Person(Long id, String name, String lastName) { 19 this.id = id; 20 this.name = name; 21 this.lastName = lastName; 22 } 23 /** 24 * @返回 id 25 */ 26 public Long getId() { 27 return id; 28 } 29 30 /** 31 * @id的setter方法 32 */ 33 public void setId(Long id) { 34 this.id = id; 35 } 36 37 /** 38 * @返回 lastName 39 */ 40 public String getLastName() { 41 return lastName; 42 } 43 44 /** 45 * @ lastName 的setter方法 46 */ 47 public void setLastName(String lastName) { 48 this.lastName = lastName; 49 } 50 51 /** 52 * @返回 name 53 */ 54 public String getName() { 55 return name; 56 } 57 58 /** 59 * @name属性的setter方法 60 */ 61 public void setName(String name) { 62 this.name = name; 63 } 64 }
【代码剖析】在上述代码中,首先定义了三个成员变量id、name和lastName,然后又创建了三个构造函数,最后为所有的成员变量创建getter和setter方法。
6.8.3 编写action类
JasperAction创建了一些人员的列表。JasperCompileManager会将jrxml模板编译为.jasper文件。在实际程序中不要这样做,不需要在每个请求中都把jrxml文件编译为jasper文件,只需要执行一次就可以了,发布的时候直接使用jasper文件就可以了,如实例6-30所示。
【实例6-30】关于action类:ActionSupport.java
01 package jasperreports; 02 import java.io.File; 03 import java.util.*; 04 import net.sf.jasperreports.engine.JasperCompileManager; 05 import org.apache.struts2.ServletActionContext; 06 public class ActionSupport extends com.opensymphony.xwork2.ActionSupport { 07 // 这个list是作为数据源存在的 08 private List myList; 09 public String execute() throws Exception { //重写执行方法 10 Person p1 = new Person(new Long(1), "Patrick", "Lightbuddie"); 11 Person p2 = new Person(new Long(2), "Jason", "Carrora"); 12 Person p3 = new Person(new Long(3), "Alexandru", "Papesco"); 13 Person p4 = new Person(new Long(4), "Jay", "Boss"); 14 /* 15 * 把数据保存到一个list中去, 一般情况下这些数据可能来自于数据库, 本例 16 子中为了简单直接在程序中赋值 17 */ 18 myList = new ArrayList(); 19 myList.add(p1); 20 myList.add(p2); 21 myList.add(p3); 22 myList.add(p4); 23 /* 24 * 把 xml jasper 模板编译为一个jasper文件。在实际程序中不要这样做, 不需 25 要每个请求都把jrxml文件编译为jasper文件 26 * 直接使用jasper文件就可以了 27 */ 28 try { 29 String reportSource; 30 reportSource = ServletActionContext.getServletContext() 31 .getRealPath("/jasper/jasper_template.jrxml"); 32 File parent = new File(reportSource).getParentFile(); 33 JasperCompileManager.compileReportToFile(reportSource, new 34 File( 35 parent, 36 "jasper_template.jasper").getAbsolutePath()); 37 } catch (Exception e) { 38 e.printStackTrace(); 39 return ERROR; 40 } 41 // 返回成功字符串 42 return SUCCESS; 43 } 44 /** 45 * @return 返回 myList. 46 */ 47 public List getMyList() { //关于变量myList的getter方法 48 return myList; 49 } 50 }
【代码剖析】在上述代码中为了方便,手动创建了一个包含4个Person对象的myList集合,然后通过相关方法把jrxml模块编译成一个jasper文件,最后返回成功字符串SUCCESS。为了便于操作,还专门创建了一名为myList的成员变量,并设置该变量的getter方法。
6.8.4 编写Jasper模板
JR使用一种特殊的XML页面来定义模板,它会被编译为.jasper文件。这些模板将会被用来设计结果报表。本例中展示的是一个手写的版本,对于更加复杂的模板建议使用Jasper相关的GUI设计器(如ireport)来定义。
将文件存储到工程的目录下的“WW_WEBAPP/jasper”目录(此目录名是固定的不能改变),命名为“jasper_template.jrxml”。最重要的:声明了name和lastName字段(这两个属性来自Person.class)。这意味着现在可以在Jasper模板中使用这些字段。
定义了两个表头(NAME和LASTNAME),然后将字段添加到一行的detail band。detail band将会从人员的List中迭代。这是JR的默认行为,所以如果想显示人员的更多信息,把它们添加到这个band中。
在detail band使用了
$F{name}
这意味着JR会询问Struts如何获取字段的值。将会从Struts值栈中寻找这些值(寻找人员,调用getName()这个getter),然后返回它后面的:
$F{lastName}
余下部分的大部分标记用来定义布局,如实例6-31所示。
【实例6-31】JasperReports模板:jasper_template.jrxml
01 <?xml version="1.0"?> 02 <!DOCTYPE jasperReport 03 PUBLIC "-//JasperReports//DTD Report Design//EN" 04 "http://jasperreports.sourceforge.net/dtds/jasperreport.dtd"> 05 <jasperReport name="jasper_test"> 06 <!--our fields--> 07 <field name="name" class="java.lang.String"/> 08 <field name="lastName" class="java.lang.String"/> 09 <title> 10 <band height="50"> 11 <staticText> 12 <reportElement x="0" y="0" style="width:180" height="15"/> 13 <textElement/> 14 <text> 15 <![CDATA[Webwork JasperReports Sample]]> 16 </text> 17 </staticText> 18 </band> 19 </title> 20 <pageHeader> 21 <band></band> 22 </pageHeader> 23 <columnHeader> 24 <band height="20"> 25 <staticText> 26 <reportElement x="180" y="0" style="width:180" height="20"/> 27 <textElement> 28 <font isUnderline="true"/> 29 </textElement> 30 <text> 31 <![CDATA[NAME]]> 32 </text> 33 </staticText> 34 <staticText> 35 <reportElement x="360" y="0" style="width:180" height="20"/> 36 <textElement> 37 <font isUnderline="true"/> 38 </textElement> 39 <text> 40 <![CDATA[LASTNAME]]> 41 </text> 42 </staticText> 43 </band> 44 </columnHeader> 45 <detail> 46 <band height="20"> 47 <textField> 48 <reportElement x="180" y="0" style="width:180" height="15"/> 49 <textElement/> 50 <textFieldExpression> 51 <![CDATA[$F{name}]]> 52 </textFieldExpression> 53 </textField> 54 <textField> 55 <reportElement x="360" y="0" style="width:180" height="15"/> 56 <textElement/> 57 <textFieldExpression> 58 <![CDATA[$F{lastName}]]> 59 </textFieldExpression> 60 </textField> 61 </band> 62 </detail> 63 <columnFooter> 64 <band></band> 65 </columnFooter> 66 <pageFooter> 67 <band height="15"> 68 <staticText> 69 <reportElement x="0" y="0" style="width:40" height="15"/> 70 <textElement/> 71 <text> 72 <![CDATA[Page:]]> 73 </text> 74 </staticText> 75 <textField> 76 <reportElement x="40" y="0" style="width:100" height="15"/> 77 <textElement/> 78 <textFieldExpression class="java.lang.Integer"> 79 <![CDATA[$V{PAGE_NUMBER}]]> 80 </textFieldExpression> 81 </textField> 82 </band> 83 </pageFooter> 84 <summary> 85 <band></band> 86 </summary> 87 </jasperReport>
【代码剖析】在上述代码中大部分内容都是自动生成的,所以这里就不详细介绍了。
6.8.5 配置struts.xml
将action添加到struts.xml,给这个action配置一个类型为jasper的result。Jasper类型的result不是Struts2默认就提供的,而是作为一个plugin出现,所以在配置中要继承包jasperreports-default,如实例6-32所示。
【实例6-32】JasperReports配置文件:struts.xml
01 <!DOCTYPE struts PUBLIC 02 "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" 03 "http://struts.apache.org/dtds/struts-2.0.dtd"> 04 <struts> 05 <!--JasperReports在struts.xml中的配置 --> 06 <package name="default" extends="struts-default,jasperreports-default"> 07 <action name="PDF" class="jasperreports.ActionSupport"> 08 <result name="success" type="jasper"> 09 <param 10 name="location">/jasper/jasper_template.jasper</param> 11 <param name="dataSource">myList</param> 12 <param name="format">PDF</param> 13 </result> 14 </action> <!--当请求参数为HTML时--> 15 <action name="HTML" class="jasperreports.ActionSupport"> 16 <result name="success" type="jasper"> 17 <param 18 name="location">/jasper/jasper_template.jasper</param> 19 <param name="dataSource">myList</param> 20 <param name="format">HTML</param> 21 </result> 22 </action> <!--当请求参数为XML时--> 23 <action name="XML" class="jasperreports.ActionSupport"> 24 <result name="success" type="jasper"> 25 <param 26 name="location">/jasper/jasper_template.jasper</param> 27 <param name="dataSource">myList</param> 28 <param name="format">XML</param> 29 </result> 30 </action> <!--当请求参数为CSV时--> 31 <action name="CSV" class="jasperreports.ActionSupport"> 32 <result name="success" type="jasper"> 33 <param 34 name="location">/jasper/jasper_template.jasper</param> 35 <param name="dataSource">myList</param> 36 <param name="format">CSV</param> 37 </result> 38 </action> <!--当请求参数为XLS时--> 39 <action name="XLS" class="jasperreports.ActionSupport"> 40 <result name="success" type="jasper"> 41 <param 42 name="location">/jasper/jasper_template.jasper</param> 43 <param name="dataSource">myList</param> 44 <param name="format">XLS</param> 45 </result> 46 </action> 47 </package> 48 </struts>
【代码剖析】
1)将ActionSupport注册为“PDF”这意味着可以在浏览器中通过PDF.action发出请求来执行这个Action。
2)当ActionSupport执行正确,由于继承了jasperreports-default,它就已经被配置好了。所以报表类型已经配置好。
<result name="success" type="jasper">
3)这种result type根据参数params配置。配置如下:
<param name="location">/jasper/jasper_template.jasper </param>
4)这个参数定义了编译好的jasper文件的位置,它将被Struts根据的数据源dataSource填充。数据源的名称就是需要调用的getter的名字(上面的配置会调用JasperAction中的getMyList()方法):
<param name="dataSource">myList</param>
5)它将被用来以数据填充模板,这一行指定了jasper被转换成的文件格式。值可以是PDF、CSV、XLS和HTML:
<param name="format">PDF</param>
注意
生成HTML报表时jasperreports会引用到WebRoot/images下一个名为px的文件(没有扩展名)。可以将jasperreports-2.0.2\net\sf\jasperreports\engine\images下的pixel.GIF文件复制到这个目录下并重命名为px。