Jasper Reports 简明教程

Create SubReports

子报表是 JasperReports 的一项不错的功能。此功能允许在另一个报表内合并报表,即一个报表可以是另一个报表的子报表。子报表帮助我们保持报表设计简单,因为我们可以创建许多简单的报表并将其封装到一个主报表中。子报表像普通报表一样进行编译和填充。在合并到另一个报表模板时,任何报表模板都可以用作子报表,而无需对(报表模板)中的任何内容进行更改。

Subreports are one of the nice features of the JasperReports. This feature allows incorporating a report within another report, that is, one report can be a subreport of another. Subreports help us keep report designs simple, as we can create many simple reports and encapsulate them into a master report. Subreports are compiled and filled just like normal reports. Any report template can be used as a subreport when incorporated into another report template, without anything changed inside (of the report template).

子报表就像普通报表模板。实际上,它们是 net.sf.jasperreports.engine.JasperReport 对象,在编译 net.sf.jasperreports.engine.design.JasperDesign 对象后获得这些对象。

Subreports are like normal report templates. They are in fact net.sf.jasperreports.engine.JasperReport objects, which are obtained after compiling a net.sf.jasperreports.engine.design.JasperDesign object.

<subreport> Element

在将子报表引入主报表时,会使用 <subreport> 元素。下面是 <subreport> JRXML 元素的子元素列表。

A <subreport> element is used when introducing subreports into master reports. Here is the list of sub-elements in the <subreport> JRXML element.

  1. <reportElement>

  2. <parametersMapExpression> − This is used to pass a map containing report parameters to the subreport. The map is usually obtained from a parameter in the master report, or by using the built-in REPORTS_PARAMETERS_MAP parameter to pass the parent report’s parameters to the subreport. This expression should always return a java.util.Map object in which the keys are the parameter names.

  3. <subreportParameter> − This element is used to pass parameters to the subreport. It has an attribute name, which is mandatory.

  4. <connectionExpression > − This is used to pass a java.sql.Connection to the subreport. It is used only when the subreport template needs a database connection during report filling phase.

  5. <dataSourceExpression> − This is used to pass a datasource to the subreport. This datasource is usually obtained from a parameter in the master report or by using the built-in REPORT_DATA_SOURCE parameter to pass the parent report’s datasource to the subreport.

  6. The elements (connectionExpression and dataSourceExpression) cannot be present at the same time in a <subreport> element declaration. This is because we cannot supply both a data source and a connection to the subreport. We must decide on one of them and stick to it.

  7. <returnValue> − This is used to assign the value of one of the subreport’s variables to one of the master report’s variables. This sub element has attributes as follows − subreportVariable − This attribute specifies the name of the subreport variable whose value is to be returned. toVariable − This attribute specifies the name of the parent report variable whose value is to be copied/incremented with the value from the subreport. calculation − This attribute can take values : Nothing, Count, DistinctCount, Sum, Average, Lowest, Highest, StandardDeviation, Variance. Default value for attribute calculation is "Nothing". incrementerFactoryClass − This attribute specifies the factory class for creating the incrementer instance.

  8. <subreportExpression> − This indicates where to find the compiled report template for the subreport. This element has a class attribute. The class attribute can take any of these values:java.lang.String, java.io.File, java.net.URL, java.io.InputStream, net.sf.jasperreports.engine.JasperReport. Default value is java.lang.String.

  9. isUsingCache − This is an attribute of the <subreport> element. This is a Boolean, when set to true, the reporting engine will try to recognize previously loaded subreport template objects, using their specified source. This caching functionality is available only for subreport elements that have expressions returning java.lang.String objects as the subreport template source, representing file names, URLs, or classpath resources.

Example

让我们举一个简单的示例来说明使用 JRDataSource 创建子报表的步骤。首先,我们编写两个新的报表模板,一个是子报表,另一个是主报表。子报表 (address_report_template.jrxml) 模板的内容如下。将其保存到 C:\tools\jasperreports-5.0.1\test 目录中。

Let take up a simple example to demonstrate creation of subreports using JRDataSource. Let’s first write two new report templates, one being subreport and the other Master report. The contents of the subreport (address_report_template.jrxml) template is as given below. Save it to C:\tools\jasperreports-5.0.1\test directory.

<?xml version = "1.0" encoding = "UTF-8"?>
<jasperReport
   xmlns = "http://jasperreports.sourceforge.net/jasperreports"
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation = "http://jasperreports.sourceforge.net/jasperreports
   http://jasperreports.sourceforge.net/xsd/jasperreport.xsd"
   name = "address_report_template" pageWidth = "175" pageHeight = "842"
   columnWidth = "175" leftMargin = "0" rightMargin = "0"
   topMargin = "0" bottomMargin = "0">

   <field name = "city" class = "java.lang.String"/>
   <field name = "street" class = "java.lang.String"/>

   <background>
      <band splitType = "Stretch"/>
   </background>

   <title>
      <band height = "20" splitType = "Stretch">

         <staticText>
            <reportElement x = "0" y = "0" width = "100" height = "20"/>

            <textElement>
               <font size = "14" isBold = "true"/>
            </textElement>

            <text><![CDATA[Addresses]]></text>
         </staticText>

      </band>
   </title>

   <pageHeader>
      <band height = "12" splitType = "Stretch"/>
   </pageHeader>

   <columnHeader>
      <band height = "12" splitType = "Stretch"/>
   </columnHeader>

   <detail>
      <band height = "27" splitType = "Stretch">

         <textField>
            <reportElement x = "0" y = "0" width = "120" height = "20"/>

            <textElement>
               <font size = "12" isBold = "true"/>
            </textElement>

            <textFieldExpression class = "java.lang.String">
               <![CDATA[$F{city}+" Address:"]]>
            </textFieldExpression>
         </textField>

         <textField isStretchWithOverflow = "true">
            <reportElement x = "120" y = "0" width = "435" height = "20"/>

            <textElement>
               <font size = "12"/>
            </textElement>

            <textFieldExpression class = "java.lang.String">
               <![CDATA[$F{street}]]>
            </textFieldExpression>
         </textField>

      </band>
   </detail>

   <columnFooter>
      <band height = "8" splitType = "Stretch"/>
   </columnFooter>

   <pageFooter>
      <band height = "11" splitType = "Stretch"/>
   </pageFooter>

   <summary>
      <band height = "9" splitType = "Stretch"/>
   </summary>

</jasperReport>

由于我们使用数据源,因此我们需要编写一个相应的 POJO 文件 *SubReportBean.java *,如下所示。将其保存到目录 C:\tools\jasperreports-5.0.1\test\src\com\tutorialspoint −

As we use a data source, we need to write a corresponding POJO file *SubReportBean.java * as shown below. Save it to directory C:\tools\jasperreports-5.0.1\test\src\com\tutorialspoint −

package com.tutorialspoint;

public class SubReportBean {
   private String city;
   private String street;

   public String getCity() {
      return city;
   }

   public void setCity(String city) {
      this.city = city;
   }

   public String getStreet() {
      return street;
   }

   public void setStreet(String street) {
      this.street = street;
   }
}

在此,我们声明了两个字段 'city' 和 'street',并定义了相应的 getter 和 setter 方法。

Here, we have declared two fields 'city' and 'street' and respective getter and setter methods are defined.

现在,我们来更新现有的 DataBean 文件。我们将添加一个新字段 subReportBeanList,它是一个 java.util.List。此字段将保存 SubReportBean 对象的列表。DataBean 文件的内容如下。将其保存到目录 C:\tools\jasperreports-5.0.1\test\src\com\tutorialspoint。

Now, let’s update our existing DataBean file. We will add a new field subReportBeanList, which is a java.util.List. This field will hold the list of SubReportBean objects. The contents of the file DataBean are as below. Save it to directory C:\tools\jasperreports-5.0.1\test\src\com\tutorialspoint.

package com.tutorialspoint;

import java.util.List;

public class DataBean {
   private String name;
   private String country;
   private List<SubReportBean> subReportBeanList;

   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }

   public String getCountry() {
      return country;
   }

   public void setCountry(String country) {
      this.country = country;
   }

   public List<SubReportBean> getSubReportBeanList() {
      return subReportBeanList;
   }

   public void setSubReportBeanList(List<SubReportBean> subReportBeanList) {
      this.subReportBeanList = subReportBeanList;
   }
}

我们现在更新文件 C:\tools\jasperreports-5.0.1\test\src\com\tutorialspoint*DataBeanList.java*。该文件的内容如下 −

Let’s now update the file C:\tools\jasperreports-5.0.1\test\src\com\tutorialspoint*DataBeanList.java*. The contents of this file are as −

package com.tutorialspoint;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class DataBeanList {
   public ArrayList<DataBean> getDataBeanList() {

      // Create sub report data
      SubReportBean subBean1 = new SubReportBean();
      subBean1.setCity("Mumbai");
      subBean1.setStreet("M.G.Road");
      SubReportBean subBean2 = new SubReportBean();
      subBean2.setCity("New York");
      subBean2.setStreet("Park Street");
      SubReportBean subBean3 = new SubReportBean();
      subBean3.setCity("San Fransisco");
      subBean3.setStreet("King Street");

      ArrayList<DataBean> dataBeanList = new ArrayList<DataBean>();

      // Create master report data
      dataBeanList.add(produce("Manisha", "India",
         Arrays.asList(subBean1)));
      dataBeanList.add(produce("Dennis Ritchie", "USA",
         Arrays.asList(subBean2)));
      dataBeanList.add(produce("V.Anand", "India",
         Arrays.asList(subBean1)));
      dataBeanList.add(produce("Shrinath", "California",
         Arrays.asList(subBean3)));

      return dataBeanList;
   }

   /*
    * This method returns a DataBean object,
    * with name, country and sub report
    * bean data set in it.
    */
   private DataBean produce(String name, String country,
      List<SubReportBean> subBean) {
      DataBean dataBean = new DataBean();

      dataBean.setName(name);
      dataBean.setCountry(country);
      dataBean.setSubReportBeanList(subBean);

      return dataBean;
   }
}

在上述文件中的 produce() 方法中,我们设置了 SubReportBean 的列表。

In the method produce() in the above file, we are setting the list of SubReportBean.

现在,我们来编写一个新的主报表模板(jasper_report_template.jrxml)。将此文件保存到目录 C:\tools\jasperreports-5.0.1\test 。该文件的内容如下 −

Now, let’s write a new master report template (jasper_report_template.jrxml). Save this file to directory C:\tools\jasperreports-5.0.1\test. The contents for this file are as below −

<?xml version = "1.0" encoding = "UTF-8"?>
<jasperReport xmlns = "http://jasperreports.sourceforge.net/jasperreports"
   xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation = "http://jasperreports.sourceforge.net/jasperreports
   http://jasperreports.sourceforge.net/xsd/jasperreport.xsd"
   name = "jasper_report_template" language = "groovy" pageWidth = "595"
   pageHeight = "842" columnWidth ="555" leftMargin = "20" rightMargin = "20"
   topMargin = "20" bottomMargin = "20">

   <parameter name = "SUBREPORT_DIR" class = "java.lang.String" isForPrompting = "false">
      <defaultValueExpression>
         <![CDATA["C:\\tools\\jasperreports-5.0.1\\test\\"]]>
      </defaultValueExpression>
   </parameter>

   <field name = "country" class = "java.lang.String"/>
   <field name = "name" class = "java.lang.String"/>
   <field name = "subReportBeanList" class = "java.util.List"/>

   <background>
      <band splitType = "Stretch"/>
   </background>

   <title>
      <band height = "35" splitType = "Stretch">

         <staticText>
            <reportElement x = "0" y = "0" width = "204" height = "34"/>

            <textElement>
               <font size = "26" isBold = "true"/>
            </textElement>

            <text><![CDATA[Contact Report]]></text>
         </staticText>

      </band>
   </title>

   <pageHeader>
      <band height = "17" splitType = "Stretch"/>
   </pageHeader>

   <columnHeader>
      <band height = "21" splitType = "Stretch"/>
   </columnHeader>

   <detail>
      <band height = "112" splitType = "Stretch">

         <staticText>
            <reportElement x = "0" y = "0" width = "100" height = "20"/>

            <textElement>
               <font size = "12" isBold = "true"/>
            </textElement>

            <text><![CDATA[Name:]]></text>
         </staticText>

         <staticText>
            <reportElement x = "0" y = "20" width = "100" height = "20"/>

            <textElement>
               <font size = "12" isBold = "true"/>
            </textElement>

            <text><![CDATA[Country:]]></text>
         </staticText>

         <textField>
            <reportElement x = "104" y = "0" width = "277" height = "20"/>

            <textElement>
               <font size = "12"/>
            </textElement>

            <textFieldExpression class = "java.lang.String">
               <![CDATA[$F{name}]]>
            </textFieldExpression>
         </textField>

         <textField>
            <reportElement x = "104" y = "20" width = "277" height = "20"/>

            <textElement>
               <font size = "12"/>
            </textElement>

            <textFieldExpression class = "java.lang.String">
               <![CDATA[$F{country}]]>
            </textFieldExpression>
         </textField>

         <subreport>
            <reportElement positionType = "Float" x = "335" y = "25" width = "175"
               height = "20" isRemoveLineWhenBlank = "true" backcolor = "#99ccff"/>

            <dataSourceExpression>
               new net.sf.jasperreports.engine.data.JRBeanCollectionDataSource
                  ($F{subReportBeanList})
            </dataSourceExpression>

            <subreportExpression class = "java.lang.String">
               <![CDATA[$P{SUBREPORT_DIR} + "address_report_template.jasper"]]>
            </subreportExpression>
         </subreport>

         <line>
            <reportElement x = "0" y = "50" width = "550" height = "1"/>
         </line>

      </band>
   </detail>

   <columnFooter>
      <band height = "19" splitType = "Stretch"/>
   </columnFooter>

   <pageFooter>
      <band height = "18" splitType = "Stretch"/>
   </pageFooter>

   <summary>
      <band height = "14" splitType = "Stretch"/>
   </summary>

</jasperReport>

在上述模板中,我们定义了一个新参数“SUBREPORT_DIR”,该参数定义了子报表的路径。我们定义了一个类型为 java.util.List 的字段 subReportBeanList,它对应于 DataBean 文件中的属性。元素 <subreport> 有子元素 <dataSourceExpression>。我们已将列表 subReportBeanList 放入 JRBeanCollectionDataSource 的实例中。在子元素 <subreportExpression/> 中,我们给出了子报表名称(AddressReport.jasper)。

In the above template, we have defined a new parameter "SUBREPORT_DIR," which defines the path of the subreport. We have defined a field subReportBeanList of type java.util.List, which corresponds to property in the file DataBean. The element <subreport> has sub-element <dataSourceExpression>. We have put the list subReportBeanList in an instance of JRBeanCollectionDataSource. In the sub-element <subreportExpression/>, we have given the subreport name (AddressReport.jasper).

现在,我们来编写一个新类 CreateReport 来编译和执行我们的报表模板。文件 C:\tools\jasperreports-5.0.1\test\src\com\tutorialspoint\CreateReport.java 的内容如下 −

Now, let’s write a new class CreateReport to compile and execute our report template. The contents of file C:\tools\jasperreports-5.0.1\test\src\com\tutorialspoint\CreateReport.java are as given below −

package com.tutorialspoint;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JasperCompileManager;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;

public class CreateReport {

   public static void main(String[] args) {
      String masterReportFileName = "C://tools/jasperreports-5.0.1/test"
         + "/jasper_report_template.jrxml";
      String subReportFileName = "C://tools/jasperreports-5.0.1/test"
         + "/AddressReport.jrxml";
      String destFileName = "C://tools/jasperreports-5.0.1/test"
         + "/jasper_report_template.JRprint";

      DataBeanList DataBeanList = new DataBeanList();
      ArrayList<DataBean> dataList = DataBeanList.getDataBeanList();
      JRBeanCollectionDataSource beanColDataSource = new
         JRBeanCollectionDataSource(dataList);

      try {
         /* Compile the master and sub report */
         JasperReport jasperMasterReport = JasperCompileManager
            .compileReport(masterReportFileName);
         JasperReport jasperSubReport = JasperCompileManager
            .compileReport(subReportFileName);

         Map<String, Object> parameters = new HashMap<String, Object>();
         parameters.put("subreportParameter", jasperSubReport);
         JasperFillManager.fillReportToFile(jasperMasterReport,
            destFileName, parameters, beanColDataSource);

      } catch (JRException e) {

         e.printStackTrace();
      }
      System.out.println("Done filling!!! ...");
   }
}

此处,可以看到我们在编译主模板和子报表模板,并传递主报表(.jasper)文件以用于报表填充。

Here, we see that we are compiling both the master and sub report templates and passing the master report (.jasper) file for the report filling.

Report Generation

现在,我们的所有文件都准备好了,我们使用常规 ANT 构建流程来编译和执行它们。文件 build.xml 的内容(保存在目录 C:\tools\jasperreports-5.0.1\test 下)如下所示。

Now, all our files are ready, let’s compile and execute them using our regular ANT build process. The contents of the file build.xml (saved under directory C:\tools\jasperreports-5.0.1\test) are as given below.

导入文件 - baseBuild.xml 从第 Environment Setup 章中获取,并应放置在与 build.xml 相同的目录中。

The import file - baseBuild.xml is picked up from the chapter Environment Setup and should be placed in the same directory as the build.xml.

<?xml version = "1.0" encoding = "UTF-8"?>
<project name = "JasperReportTest" default = "viewFillReport" basedir = ".">
   <import file = "baseBuild.xml" />

   <target name = "viewFillReport" depends = "compile,compilereportdesing,run"
      description = "Launches the report viewer to preview the
      report stored in the .JRprint file.">

      <java classname = "net.sf.jasperreports.view.JasperViewer" fork = "true">
         <arg value = "-F${file.name}.JRprint" />
         <classpath refid = "classpath" />
      </java>
   </target>

   <target name = "compilereportdesing" description = "Compiles the JXML file and
      produces the .jasper file.">

      <taskdef name = "jrc" classname = "net.sf.jasperreports.ant.JRAntCompileTask">
         <classpath refid = "classpath" />
      </taskdef>

      <jrc destdir = ".">
         <src>
            <fileset dir = ".">
               <include name = "*.jrxml" />
            </fileset>
         </src>
         <classpath refid = "classpath" />
      </jrc>

   </target>

</project>

接下来,让我们打开命令行窗口并转到放置 build.xml 的目录。最后,执行命令 ant -Dmain-class=com.tutorialspoint.CreateReport (viewFullReport 是默认目标),如下所示 −

Next, let’s open command line window and go to the directory where build.xml is placed. Finally, execute the command ant -Dmain-class=com.tutorialspoint.CreateReport (viewFullReport is the default target) as follows −

Buildfile: C:\tools\jasperreports-5.0.1\test\build.xml

clean-sample:
   [delete] Deleting directory C:\tools\jasperreports-5.0.1\test\classes

compile:
   [mkdir] Created dir: C:\tools\jasperreports-5.0.1\test\classes
   [javac] C:\tools\jasperreports-5.0.1\test\baseBuild.xml:28:
      warning: 'includeantruntime' was not set, defaulting to
   [javac] Compiling 7 source files to C:\tools\jasperreports-5.0.1\test\classes

compilereportdesing:
   [jrc] Compiling 1 report design files.
   [jrc] log4j:WARN No appenders could be found for logger
   (net.sf.jasperreports.engine.xml.JRXmlDigesterFactory).
   [jrc] log4j:WARN Please initialize the log4j system properly.
   [jrc] log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig
      for more info.
   [jrc] File : C:\tools\jasperreports-5.0.1\test\
      jasper_report_template.jrxml ... OK.

run:
   [echo] Runnin class : com.tutorialspoint.CreateReport
   [java] Compiling Report Design ...
   [java] log4j:WARN No appenders could be found for logger
   (net.sf.jasperreports.engine.xml.JRXmlDigesterFactory).
   [java] log4j:WARN Please initialize the log4j system properly.
   [java] Done filling!!! ...

viewFillReport:
   [java] log4j:WARN No appenders could be found for logger
   (net.sf.jasperreports.extensions.ExtensionsEnvironment).
   [java] log4j:WARN Please initialize the log4j system properly.

BUILD SUCCESSFUL
Total time: 72 minutes 13 seconds

作为上述编译的结果,一个 JasperViewer 窗口会打开,如以下给出的屏幕截图所示 -

As a result of above compilation, a JasperViewer window opens up as shown in the screen given below −

subreport example

此处,可以看到显示了属性 Name、Country 和 Address。

Here, we can see that the attributes Name, Country, and Address are displayed.