Tuesday, June 12, 2012

Jasper subreport – Way to modularize jasper reports


We come across such a situation where a specific report section appears in multiple reports. Example - An external client focused company might have a contact information section in all of its reports. Company’s decision to change the format of contact information section might result in amending all the reports, involving huge manual effort.

Jasper reports provides a modularization mechanism where we can develop the working re-usable part of report independently and plug it in where ever needed.  This binding of reusable part to main report is runtime binding. So any changes in re-usable report would automatically get reflected wherever it has been used.

As an example to deal with above mentioned problem efficiently, we could have developed the contact information section as an independent report and plugged it in all other reports.

Jasper provides subreport tag to achieve modularity, where we can define how and where the included section should appear in the main report. We can pass the parent’s or derived connection, data sources or parameter map to be used for populating the included reports. 

Jasper subreport example - To demo the working of sub-reports, we will develop two reports called simpleSubreport.jrxml, simpleSubreport2.jrxml and use these modular components in simpleMaster.jrxml so that the complete report could be generated in a single shot-

simpleSubreport.jrxml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jasperReport PUBLIC "-//JasperReports//DTD Report Design//EN" "http://jasperreports.sourceforge.net/dtds/jasperreport.dtd">
<jasperReport name="SimpleSubreport" language="java" pageWidth="595" pageHeight="842" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="30" bottomMargin="30">

 <field name="subTitle" class="java.lang.String"></field>
 
 <detail>
  <band height="100">
  <textField>
  <reportElement positionType="Float" x="55" y="14" width="75" height="41"/>
  <textElement/>
  <textFieldExpression class="java.lang.String"><![CDATA[$F{subTitle}]]></textFieldExpression>
  </textField>
  </band>
 </detail>
</jasperReport> 

simpleSubreport2.jrxml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jasperReport PUBLIC "-//JasperReports//DTD Report Design//EN" "http://jasperreports.sourceforge.net/dtds/jasperreport.dtd">
<jasperReport name="SimpleSubreport2" language="java" pageWidth="200" pageHeight="842" columnWidth="200" leftMargin="0" rightMargin="0" topMargin="0" bottomMargin="0">

 <field name="subTitle" class="java.lang.String"></field>
 
 <detail>
  <band height="100">
   <staticText>
   <reportElement positionType="Float" mode="Opaque" x="36" y="8" width="100" height="16" backcolor="#ff8000"/>
   <textElement/>
   </staticText>
   
   <textField>
   <reportElement positionType="Float" x="35" y="35" width="110" height="30"/>
   <textElement/>
   <textFieldExpression class="java.lang.String"><![CDATA[$F{subTitle}]]></textFieldExpression>
   </textField>
  </band>
 </detail>
</jasperReport>

simpleMaster.jrxml
<?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="SimpleMaster" pageWidth="595" pageHeight="842" columnWidth="555" leftMargin="20" rightMargin="20" topMargin="30" bottomMargin="30">
 <parameter name="mainReportParameterMap" class="java.util.HashMap"/>

 <field name="master" class="java.lang.String"/>
 <field name="id" class="java.lang.String"/>
 <field name="id2" class="java.lang.String"/>
 <title>
  <band height="107" splitType="Stretch">
   <textField>
    <reportElement positionType="Float" x="79" y="8" width="167" height="30"/>
    <textElement/>
    <textFieldExpression class="java.lang.String"><![CDATA[$P{mainReportParameterMap}.get("title")]]></textFieldExpression>
   </textField>
   <textField>
    <reportElement positionType="Float" x="86" y="63" width="190" height="32"/>
    <textElement/>
    <textFieldExpression class="java.lang.String"><![CDATA[$F{master}]]></textFieldExpression>
   </textField>
  </band>
 </title>
 <detail>
  <band height="100" splitType="Stretch">
   <subreport>
    <reportElement positionType="Float" x="67" y="18" width="168" height="47"/>
    <subreportParameter name="id">
     <subreportParameterExpression><![CDATA[$P{mainReportParameterMap}.get($F{id})]]></subreportParameterExpression>
    </subreportParameter>
    <dataSourceExpression><![CDATA[$P{mainReportParameterMap}.get($F{id})]]></dataSourceExpression>
    <subreportExpression class="java.lang.String"><![CDATA["simpleSubreport.jasper"]]></subreportExpression>
   </subreport>
   <subreport>
    <reportElement positionType="Float" x="249" y="18" width="168" height="47"/>
    <subreportParameter name="id2">
     <subreportParameterExpression><![CDATA[$P{mainReportParameterMap}.get($F{id2})]]></subreportParameterExpression>
    </subreportParameter>
    <dataSourceExpression><![CDATA[$P{mainReportParameterMap}.get($F{id2})]]></dataSourceExpression>
    <subreportExpression class="java.lang.String"><![CDATA["simpleSubreport2.jasper"]]></subreportExpression>
   </subreport>
  </band>
 </detail>
 <pageFooter>
  <band height="15" splitType="Stretch">
   <textField>
    <reportElement x="0" y="0" width="520" height="15"/>
    <textElement textAlignment="Right"/>
    <textFieldExpression class="java.lang.Integer"><![CDATA[$V{PAGE_NUMBER}+"/"]]></textFieldExpression>
   </textField>
   <textField evaluationTime="Report">
    <reportElement x="521" y="0" width="14" height="15"/>
    <textElement textAlignment="Left"/>
    <textFieldExpression class="java.lang.Integer"><![CDATA[$V{PAGE_NUMBER}]]></textFieldExpression>
   </textField>
  </band>
 </pageFooter>
</jasperReport>

SubReportExample.java
package subreportexample;

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

import net.sf.jasperreports.engine.JRDataSource;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JasperRunManager;
import net.sf.jasperreports.engine.data.JRMapCollectionDataSource;
import utility.JasperUtility;

@SuppressWarnings("rawtypes")
public class SubReportExample {

 static String jrxmlFileLocation = "src\\main\\resources\\subreportexample\\";
 static String jasperFileLocation = "target\\classes\\subreportexample\\";
 String outputPDFFile = "target\\classes\\subreportexample\\SubReportFile.pdf";;
 final int firstDatasourceNumber = 1;
 final int secondDatasourceNumber = 2;

 /**
  * @throws JRException
  */
 @SuppressWarnings("unchecked")
 private void generateReport() throws JRException {

  List simpleSubMasterList = getMapArrayListForReport(firstDatasourceNumber);
  JRMapCollectionDataSource firstReportDataSource = new JRMapCollectionDataSource(
    simpleSubMasterList);

  simpleSubMasterList = getMapArrayListForReport(secondDatasourceNumber);
  JRMapCollectionDataSource secondReportDataSource = new JRMapCollectionDataSource(
    simpleSubMasterList);

  HashMap mainReportParameterMap = new HashMap();
  mainReportParameterMap.put("title", "Title of master report");
  mainReportParameterMap.put("subDS", firstReportDataSource);
  mainReportParameterMap.put("subDS2", secondReportDataSource);

  Map mainReportDSElementMap = new HashMap();
  mainReportDSElementMap.put("master",
    "This portion is from master report");
  mainReportDSElementMap.put("id", "subDS");
  mainReportDSElementMap.put("id2", "subDS2");

  List simpleMasterList = new ArrayList();
  simpleMasterList.add(mainReportDSElementMap);
  JRDataSource simpleDS = new JRMapCollectionDataSource(simpleMasterList);

  Map parameters = new HashMap();
  parameters.put("mainReportParameterMap", mainReportParameterMap);

  JasperRunManager.runReportToPdfFile(jasperFileLocation
    + "simpleMaster.jasper", outputPDFFile, parameters, simpleDS);
 }

 /**
  * @param firstDatasourceNumber
  * @return
  */
 @SuppressWarnings("unchecked")
 private List getMapArrayListForReport(int firstDatasourceNumber) {
  List simpleSubMasterList = new ArrayList();

  for (int i = 0; i < 30; i++) {
   Map simpleSubMasterMap = new HashMap();
   simpleSubMasterMap.put("subTitle", "This is subtitile no " + i
     + " of data source no " + firstDatasourceNumber);
   simpleSubMasterList.add(simpleSubMasterMap);
  }
  return simpleSubMasterList;
 }

 public static void main(String[] args) throws JRException {
  compileJrxmlFiles();
  new SubReportExample().generateReport();
  System.out.println("done");
 }

 private static void compileJrxmlFiles() throws JRException {
  JasperUtility.compileAndGenerateJasperFile(jrxmlFileLocation,
    jasperFileLocation, "simpleMaster", "simpleSubreport",
    "simpleSubreport2");
 }
}

Sample Project -
Sample project can be downloaded from here

 You will need maven-3 along with java 5 or higher version, setup in your machine to run the sample.
Kindly follow the below mentioned steps to run the sample and get the report -
  1. Download & unzip the project
  2. Run- mvn clean eclipse:eclipse install in project's home directory
  3. Import the application in eclipse
  4. Execute SubReportExample.java file
  5. Sample pdf will be generated at target\\classes\\subreportexample\\SubReportFile.pdf location.
References
http://salilstock.blogspot.in/2012/05/jasperreport-open-source-java-reporting.html
http://salilstock.blogspot.in/2012/05/including-page-number-and-page-count-in.html

1 comment:

  1. Please contact me to discuss jeff_mire(at)yahoo.com

    ReplyDelete