JasperReports multiple data sources

In this tutorial, we show how use multiple data sources in a report generated with JasperReports library.

JasperReports is an open-source reporting library. It can create reports in various formats including PDF, HTML, XLS, or CSV. JasperReports creates page-oriented, ready-to-print documents.

Multiple data sources

The following application displays data from two different data sources in a report.

$ tree
.
├── pom.xml
└── src
    └── main
        ├── java
        │   └── com
        │       └── zetcode
        │           ├── bean
        │           │   ├── Car.java
        │           │   └── Country.java
        │           └── main
        │               ├── CommandLineRunner.java
        │               └── JasperMultipleDataSources.java
        └── resources
            └── report2.xml

This is the project structure.

Car.java
package com.zetcode.bean;

public class Car {
    
    private Long id;
    private String name;
    private int price;
    
    public Car() {}

    public Car(Long id, String name, int price) {
        this.id = id;
        this.name = name;
        this.price = price;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Car{" + "id=" + id + ", name=" + 
                name + ", price=" + price + '}';
    }
}

This is a Car bean class. It contains car id, name, and price attributes.

Country.java
package com.zetcode.bean;

public class Country {
    
    private Long id;
    private String name;
    private int population;

    public Country(Long id, String name, int population) {
        this.id = id;
        this.name = name;
        this.population = population;
    }
    
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public int getPopulation() {
        return population;
    }

    public void setPopulation(int population) {
        this.population = population;
    }
}

This is a Country bean class. It contains country id, name, and population attributes.

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
http://maven.apache.org/xsd/maven-4.0.0.xsd">
    
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.zetcode</groupId>
    <artifactId>JasperMultipleDataSources</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>
    
    <dependencies>
        
        <dependency>
            <groupId>net.sf.jasperreports</groupId>
            <artifactId>jasperreports</artifactId>
            <version>6.4.3</version>
        </dependency>
                
    </dependencies>
    
    <name>JasperMultipleDataSources</name>
    
</project>

The Maven pom.xml file contains the jasperreports dependency.

report2.xml
<?xml version = "1.0" encoding = "UTF-8"?>
<!DOCTYPE jasperReport PUBLIC "//JasperReports//DTD Report Design//EN"
   "http://jasperreports.sourceforge.net/dtds/jasperreport.dtd">

<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="freport" pageWidth="595" pageHeight="842" 
              columnWidth="555" leftMargin="20" rightMargin="20"
              topMargin="20" bottomMargin="20">
    
<style name="field" fontSize="11" />
<style name="textRight" style="field" hAlign="Right" />
    
<subDataset name="dataset1">
    <field name="id" class="java.lang.Long"/>
    <field name="name" class="java.lang.String"/>
    <field name="price" class="java.lang.Integer"/> 
</subDataset>    

<subDataset name="dataset2">
    <field name="id" class="java.lang.Long"/>
    <field name="name" class="java.lang.String"/>
    <field name="population" class="java.lang.Integer"/> 
</subDataset> 

<parameter name="datasource1" class="java.util.List"/>
<parameter name="datasource2" class="java.util.List"/>

<detail>
    <band height="15">
        
        <componentElement>
            <reportElement x="0" y="0" width="100" height="15"/>
            <jr:list xmlns:jr="http://jasperreports.sourceforge.net/jasperreports/components" 
                        xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports/components 
                                            http://jasperreports.sourceforge.net/xsd/components.xsd">
                
                <datasetRun subDataset="dataset2">
                    <dataSourceExpression><![CDATA[new net.sf.jasperreports.engine.data.JRBeanCollectionDataSource($P{datasource2})]]></dataSourceExpression>
                </datasetRun>
                
                <jr:listContents height="15" width="220">
                    <textField>
                        <reportElement x="0" y="0" width="20" height="15" style="field" />
        
                        <textElement />
        
                        <textFieldExpression class="java.lang.Long"><![CDATA[$F{id}]]></textFieldExpression>
                    </textField>       
        
                    <textField>
                        <reportElement x="50" y="0" width="80" height="15" style="field" />
        
                        <textElement />
        
                        <textFieldExpression class="java.lang.String"><![CDATA[$F{name}]]></textFieldExpression>
                    </textField>               
        
                    <textField>
                        <reportElement x="130" y="0" width="80" height="15" style="textRight" />
                        <textElement />
        
                        <textFieldExpression class="java.lang.Integer"><![CDATA[$F{population}]]></textFieldExpression>
                    </textField> 
            
                </jr:listContents>   
            </jr:list>
        </componentElement>   
        
        <componentElement>
            <reportElement x="250" y="0" width="100" height="15"/>
            <jr:list xmlns:jr="http://jasperreports.sourceforge.net/jasperreports/components" 
                        xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports/components 
                                            http://jasperreports.sourceforge.net/xsd/components.xsd">
                
                <datasetRun subDataset="dataset1">
                    <dataSourceExpression><![CDATA[new net.sf.jasperreports.engine.data.JRBeanCollectionDataSource($P{datasource1})]]></dataSourceExpression>
                </datasetRun>
                
                <jr:listContents height="15" width="220">
                    <textField>
                        <reportElement x="0" y="0" width="20" height="15" style="field" />
        
                        <textElement />
        
                        <textFieldExpression class="java.lang.Long"><![CDATA[$F{id}]]></textFieldExpression>
                    </textField>       
        
                    <textField>
                        <reportElement x="50" y="0" width="80" height="15" style="field" />
        
                        <textElement />
        
                        <textFieldExpression class="java.lang.String"><![CDATA[$F{name}]]></textFieldExpression>
                    </textField>               
        
                    <textField>
                        <reportElement x="130" y="0" width="80" height="15" style="textRight" />
                        <textElement />
        
                        <textFieldExpression class="java.lang.Integer"><![CDATA[$F{price}]]></textFieldExpression>
                    </textField> 
            
                </jr:listContents>   
            </jr:list>
        </componentElement>              
        
    </band>
</detail>

</jasperReport>

This is the report template file. The report contains two list components. The list components load data from the list parameters passed to the report.

<subDataset name="dataset1">
    <field name="id" class="java.lang.Long"/>
    <field name="name" class="java.lang.String"/>
    <field name="price" class="java.lang.Integer"/> 
</subDataset>    

<subDataset name="dataset2">
    <field name="id" class="java.lang.Long"/>
    <field name="name" class="java.lang.String"/>
    <field name="population" class="java.lang.Integer"/> 
</subDataset>  

We have two subdatasets; each of them has three fields. The fields are mapped to the attributes of the Java beans.

<parameter name="datasource1" class="java.util.List"/>
<parameter name="datasource2" class="java.util.List"/> 

The report accepts two java.util.List parameters.

<componentElement>
    <reportElement x="0" y="0" width="100" height="15"/>
    <jr:list xmlns:jr="http://jasperreports.sourceforge.net/jasperreports/components" 
...

There are two jr:list components in the report. Each subDataset is added to one list component.

<datasetRun subDataset="dataset2">
    <dataSourceExpression><![CDATA[new net.sf.jasperreports.engine.data.JRBeanCollectionDataSource($P{datasource2})]]></dataSourceExpression>
</datasetRun>

The subDataset is executed in the datasetRun tag. The list parameter is passed to the JRBeanCollectionDataSource. We refer to the parameter with the $P{} syntax.

<jr:listContents height="15" width="220">
    <textField>
        <reportElement x="0" y="0" width="20" height="15" style="field" />

        <textElement />

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

The fields are passed to the textField components, which are added to the jr:listContents tag. The field data is shown with the $F{} syntax.

JasperMultipleDataSources.java
package com.zetcode.main;

import com.zetcode.bean.Car;
import com.zetcode.bean.Country;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.sf.jasperreports.engine.JREmptyDataSource;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JasperCompileManager;
import net.sf.jasperreports.engine.JasperExportManager;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperReport;

public class JasperMultipleDataSources {

    public void start() throws JRException {

        String xmlFile = "src/main/resources/report2.xml";
        JasperReport jreport = JasperCompileManager.compileReport(xmlFile);

        List<Car> cars = new ArrayList<>();
        
        cars.add(new Car(1L, "Audi", 52642));
        cars.add(new Car(2L, "Mercedes", 57127));
        cars.add(new Car(3L, "Skoda", 9000));
        cars.add(new Car(4L, "Volvo", 29000));
        cars.add(new Car(5L, "Bentley", 350000));
        cars.add(new Car(6L, "Citroen", 21000));
        cars.add(new Car(7L, "Hummer", 41400));
        cars.add(new Car(8L, "Volkswagen", 21600));
        
        List<Country> countries = new ArrayList<>();
        
        countries.add(new Country(1L, "China", 1382050000));
        countries.add(new Country(2L, "India", 1313210000));
        countries.add(new Country(3L, "USA", 324666000));
        countries.add(new Country(4L, "Indonesia", 260581000));
        countries.add(new Country(5L, "Brazil", 207221000));
        countries.add(new Country(6L, "Pakistan", 196626000));
        countries.add(new Country(7L, "Nigeria", 186988000));
        countries.add(new Country(8L, "Bangladesh", 162099000));     
        
        Map params = new HashMap();
        params.put("datasource1", cars);
        params.put("datasource2", countries);

        JasperPrint jprint = JasperFillManager.fillReport(jreport,
                params, new JREmptyDataSource());
        
        JasperExportManager.exportReportToPdfFile(jprint,
                "src/main/resources/report2.pdf");        
    }
}

The JasperMultipleDataSources creates a JasperPrint file from the data sources and the report2.xml template. JasperPrint represents a page-oriented document that can be viewed, printed, or exported to other formats.

String xmlFile = "src/main/resources/report2.xml";
JasperReport jreport = JasperCompileManager.compileReport(xmlFile);

We compile the XML template file into a JasperReport. JasperReport is a compiled template ready to be filled with data.

List<Car> cars = new ArrayList<>();

cars.add(new Car(1L, "Audi", 52642));
cars.add(new Car(2L, "Mercedes", 57127));
cars.add(new Car(3L, "Skoda", 9000));
cars.add(new Car(4L, "Volvo", 29000));
...

This is a list of car objects.

List<Country> countries = new ArrayList<>();

countries.add(new Country(1L, "China", 1382050000));
countries.add(new Country(2L, "India", 1313210000));
countries.add(new Country(3L, "USA", 324666000));
...

This is a list of country objects.

Map params = new HashMap();
params.put("datasource1", cars);
params.put("datasource2", countries);

The two lists are added to the params map.

JasperPrint jprint = JasperFillManager.fillReport(jreport,
        params, new JREmptyDataSource());

A JasperPrint object is created; an object that can be viewed, printed, or exported to other formats. The params map is passed to the JasperFillManager.fillReport() method.

JasperExportManager.exportReportToPdfFile(jprint,
        "src/main/resources/report2.pdf");   

The JasperExportManager.exportReportToPdfFile() method exports the JasperPrint into a PDF file.

CommandLineRunner.java
package com.zetcode.main;

public class CommandLineRunner {

    public static void main(String[] args) throws Exception {

        JasperMultipleDataSources app = new JasperMultipleDataSources();
        app.start();
    }
}

The CommandLineRunner sets up the application.

In this tutorial, we have generated a PDF file report data from two different data sources. You might be interested in these related tutorials: JasperReports conditinal style, JasperReports filtering data, JasperReports scriptlets, JasperReports ResourceBundle, Creating a report from CSV with JasperReports, and Java tutorial.