I. Introduction

Outre les fonctionnalités offertes par JasperReports en terme d'utilisation de différents types de DataSource, les templates, les graphes, les Scriptlets... vient s'ajouter le concept des SubReports qui représente une fonctionnalité très avancée à cette API. Dans cet article nous allons voir comment insérer un SubReport (sous-rapport) dans un rapport parent et y passer différents types de paramètres.

II. SubReport et rapport parent utilisant la même source de données

2.1. Présentation

Dans cet article nous allons utiliser MS ACCESS comme SGBD. Dans un premier lieu nous allons créer une base de données qui portera pour nom testSubReports.mdb et contiendra deux tables.

Contrat :

Image non disponible

et Echeance :

Image non disponible

Ainsi, pour chaque contrat nous aurons plusieurs échéances. Nous pouvons alimenter ces deux tables par des données initiales.

Contrat :

Image non disponible

et Echeance :

Image non disponible

2.2. Configuration de la source de données

Dans cette section nous allons construire deux rapports : un master (rapport parent et un SubReport (sous-rapport) qui utiliseront la même source de données lors de l'exécution.

Créons tout d'abord un ODBC pointant sur la base de données récemment créée testSubReports.mdb et nous allons le nommer testSubReports.

Une fois l'ODBC créé, nous allons configurer une nouvelle source de données sous iReport pointant sur ce dernier. Pour créer une source de données sous iReport vous pouvez vous référer à cet article.

Image non disponible

2.3. Création des rapports

Dans ce paragraphe nous allons créer deux sous-rapports dont l'un fera appel à l'autre. Nous n'allons pas passer par l'assistant de iReport pour la création du sous-rapport afin de mieux comprendre les différentes phases.

2.3.1. Création du rapport master (parent)

Le rapport parent utilisera la source de données que nous venons de créer et utilisera la table Contrat.

Sous iReport créer un rapport vide: Fichier --> Nouveau document. Nommer ce rapport MyMasterReport.

Image non disponible

Saisir la requête qui alimentera ce rapport en cliquant sur l'îcone :

Image non disponible
 
Sélectionnez
select * from Contrat 

L'écran où nous devons saisir la requête ressemble à :

Image non disponible

Cliquer sur OK. Vous allez constater que les deux champs id et Nom de la table Contrat ont été ajoutés à la liste des champs de l'état :

Image non disponible

Nous sélectionnons ces deux champs que nous faisons glisser sur l'état sur la bande détail comme suit :

Image non disponible

Nous lançons notre rapport pour nous assurer qu'il n'y pas de problème pour le moment. Il doit avoir l'allure comme ceci :

Image non disponible

Jusqu'à maintenant nous n'avons créé que le rapport parent (le master). Mais nous n'en avons pas encore fini, nous allons y revenir après avoir fini avec le sous-rapport.

2.3.2. Création du subrapport (sous-rapport)

De la même manière qu'on a créé le rapport parent nous allons créer le sous-rapport, qu'on va nommer MySubReport. Ce Deuxième rapport attaque la table Echeance.

Image non disponible

En gros vous devrez avoir un rapport qui ressemble à ceci :

Image non disponible

Vous pouvez constater que toutes les marges de ce rapport ont été enlevées (footer, header, summary, ...). Car seule la bande détail nous intéresse.

Maintenant nous devons créer le lien qui va lier entre le rapport parent et le sous-rapport. Ce lien n'est autre qu'un paramètre qui sera créé au niveau du sous-rapport et qui sera alimenté par le rapport parent. Ce lien doit refléter la jointure entre les deux tables de la base de données, à savoir le numéro de contrat (champ NumContrat).

Créons alors un paramètre de même type que le champ de jointure (NumContrat) : String avec pour nom

Image non disponible

Revenons à la requête du sous-rapport pour la modifier comme suit :

Image non disponible

C'est bon le sous-rapport est prêt pour intégration au niveau du rapport parent.

Au niveau du rapport parent nous allons insérer, dans la bande détail, un rapport secondaire en cliquant sur l'îcone :

Image non disponible

Comme dit avant, nous n'allons pas utiliser l'assistant de iReport pour la création du sous-rapport. Dans l'écran qui se présente nous allons donc choisir : Just create the SubReport element et cliquer sur Terminer.

Image non disponible

Votre rapport doit ressembler à quelque chose comme ça :

Image non disponible

Ce nouveau rapport secondaire créé doit pointer sur le sous-rapport précedemment créé. Pour ce faire, cliquer droit sur ce nouvel élément inséré et choisir Propriétés, l'écran suivant s'ouvre :

Image non disponible

Sélectionnez l'onglet Rapport secondaire et dans Expression de connexion/source de données sélectionnez Utiliser l'expression de connexion, dans la zone de texte vous verrez que l'expression $P{REPORT_CONNECTION} a été ajoutée automatiquement. Cette expression veut dire que la connexion qu'utilisera le sous-rapport est la même connexion utilisé par le rapport parent. En effet, $P{REPORT_CONNECTION} ne fait que récupérer la connexion courante du rapport. $P{REPORT_CONNECTION} fait partie des paramètres intégrés d'un rapport qu'on ne peut ni modifier ni supprimer.

Après avoir configuré la connexion du sous-rapport, nous allons maintenant paramétrer le champ de liaison entre les deux rapports. Sur le même écran précédent, sélectionner l'onglet Rapport secondaire (autre) et choisir Paramètres de rapport secondaire en y mentionnant aussi le nom du fichier Jasper de notre sous-rapport.

Image non disponible

Cliquer sur le bouton ... montré dans la figure précédente :

Dans cet écran nous devons saisir le nom de paramètre qui servira de jointure entre les deux rapports. Le nom doit respecter la casse et doit être identique à celui créé au niveau du sous-rapport, à savoir NumContrat et aura pour valeur le champ NumContrat du rapport parent.

Image non disponible

Cliquer sur OK. La création des rapports est terminée. Nous pouvons compiler les deux rapports, mettre dans les deux jasper dans le répertoire C:\\JASPER et exécuter le rapport parent pour voir le résultat.

Image non disponible

Dans le rapport parent j'ai mis dans propriété Expression de rapport secondaire : "C:\\JASPER\\MySubReport.jasper". On pourrait ne pas fixer ce chemin dès le début et y mettre par exemple : $P{PATH_TO_SubReportS} + java.io.File.separator + "MySubReport.jasper". Dans ce cas il faut créer le paramètre PATH_TO_SubReportS et le remplir à partir de la classe Java pour spécifier le chemin où se trouve le sous-rapport.

2.4. Exécution des rapports à partir d'une classe Java

Créons tout d'abord une nouvelle application Java, dans ce cas c'est testSubReports. Nous allons créer une simple classe TestSubReport, dans le package jaub, qui va exécuter ces rapports. En fait, seul le rapport parent sera appelé, ce dernier appelera à son tour le sous-rapport.

Image non disponible

Les différents JAR de l'api JasperReports et qui vont avec doivent être ajoutés au CLASSPATH de l'application.

Dans cette classe, nous insérons le code suivant :

 
Sélectionnez
package jaub;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JasperExportManager;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.view.JasperViewer;

public class TestSubReport {

	public TestSubReport() {
	}

	public static void main(String[] args) throws JRException, IOException {
		FileInputStream fis = null;
		try {

			File file = new File("C:\\JASPER");
			fis = new FileInputStream(new File(file, "MyMasterReport.jasper"));
			JasperPrint jasperPrint = JasperFillManager.fillReport(fis, null,
					getConnection());

			// export de l'état dans un fichier pdf
			JasperExportManager.exportReportToPdfFile(jasperPrint,
					"C:\\Test1SubReport.pdf");

			// Affichage du rapport dans l'objet JasperViewer
			JasperViewer.viewReport(jasperPrint);
		} catch (FileNotFoundException ef) {
			System.out.println("fichier introuvable");
		}finally {
			if (getConnection() != null) {
				try {
					getConnection().close();
				} catch (SQLException ex) {
				}
			}

			fis.close();
		}
	}

	private static Connection getConnection() {
		Connection conn = null;

		try {
			Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
		} catch (ClassNotFoundException e) {

		}
		// connection a la base de données

		try {
			String DBurl = "jdbc:odbc:testSubReports";
			conn = DriverManager.getConnection(DBurl);
		} catch (SQLException e) {
		}

		return conn;
	}

}

A l'exécution de classe notre état est exporté dans un fichier pdf C:\Test1SubReport.pdf et affiché dans l'objet JasperViewer.

Dans cette section nous avons vu comment éditer un rapport parent appelant un sous-rapport. Ces deux rapports utilisaient la même connexion à la base de données. Dans ce qui suit, nous allons voir comment un rapport parent, utilisant une connexion à la base de données différente de celle utilisée par le sous-rapport appelé.

III. SubReport et rapport parent utilisant deux sources de données différentes

3.1. Configuration des sources de données

Dans cet article nous allons nous contenter d'utiliser le même SGBD pour le rapport parent et le sous-rapport, à savoir MS ACCESS. Ce qui va changer par rapport à la section précédente c'est qu'on va créer une deuxième base de données qui sera utilisée par le sous-rapport.

Créons alors une nouvelle base de données testSubReports2.mdb et montons là en ODBC sous le nom testSubReports2. Cette base de données contient une seule table Adresse.

Image non disponible

Cette table est alimentée par les données suivantes :

Image non disponible

3.2. Création des rapports

Tout d'abord, nous allons configurer la nouvelle source de données testSubReports2 :

Image non disponible

Choisir cette source de données comme étant la source de données par défaut de notre sous-rapport :

Image non disponible

Comme procédé dans la section précédente, nous allons créer deux nouveaux rapports MySubReport2 et MyMasterReport2.

Avant de saisir la requête SQL, nous devons créer le paramètre qui servira de liaison entre les deux rapports : NumContrat de type String.

Le MySubReport2 sera alimenté par la requête suivante :

Image non disponible

Les trois champs de la table Adresse apparaîteront dans la liste des champs du rapport :

Image non disponible

Sélectionnez les champs que vous désirez et glisser les dans la bande détail de votre rapport :

Image non disponible

Etant donné que notre sous-rapport n'utilisera pas la même source de données que le rapport parent, alors nous allons créer un paramètre MyConnection de type java.sql.Connection qui servira de source de données pour le sous-rapport, ce paramètre sera alimenté à partir de notre classe Java qu'on verra par la suite.

Image non disponible

java.sql.Connection n'existe pas comme choix dans la liste Type de classe de paramètre. Cette expression doit alors être saisie manuellement dans l'écran montré dans la figure précédente.

Le rapport parent sera créé de la même manière qu'avant, ce qui changera c'est l'élément Expression de connexion/source de données dans les propriétés du rapport secondaire inséré au niveau du rapport parent. Dans la section précédente cet élément avait pour valeur $P{REPORT_CONNECTION} dans Utiliser l'expression de connexion.

Dans ce deuxième cas nous allons mettre la valeur $P{MyConnection} dans Utiliser l'expression de connexion :

Image non disponible

Le rapport secondaire doit pointer sur le sous-rapport qui sera mis dans C:\\JASPER.

Image non disponible

Les deux rapports ont été bien créés. Vous n'avez qu'à les compiler et mettre les deux fichiers jasper MyMasterReport2.jasper et MySubReport2.jasper dans C:\\JASPER.

3.3. Exécution des rapports à partir d'une classe Java

Créons une classe TestSubReport2 dans le package jaub et insérons-y le code suivant :

 
Sélectionnez
package jaub;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JasperExportManager;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.view.JasperViewer;

public class TestSubReport2 {

	public TestSubReport2() {
	}

	public static void main(String[] args) throws IOException, JRException {
		FileInputStream fis = null;
		try {

			Map parameters = new HashMap();

			// Alimenter le paramètre MyConnection par la connexion
			// qui servira de source de données au sous-rapport

			parameters.put("MyConnection", getConnectionForSubReport());

			File file = new File("C:\\JASPER");
			fis = new FileInputStream(new File(file, "MyMasterReport.jasper"));
			JasperPrint jasperPrint = JasperFillManager.fillReport(fis,
					parameters, getConnection());

			// export de l'état dans un fichier pdf
			JasperExportManager.exportReportToPdfFile(jasperPrint,
					"C:\\Test2SubReport.pdf");

			// Affichage du rapport dans l'objet JasperViewer
			JasperViewer.viewReport(jasperPrint);
		} catch(FileNotFoundException ef){
			System.out.println("fichier introuvable");
		} finally {
			if (getConnection() != null) {
				try {
					getConnection().close();
				} catch (SQLException ex) {
				}
			}

			fis.close();
		}

	}

	private static Connection getConnection() {
		Connection conn = null;

		try {
			Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
		} catch (ClassNotFoundException e) {

		}
		// connection a la base de données

		try {
			String DBurl = "jdbc:odbc:testSubReports";
			conn = DriverManager.getConnection(DBurl);
		} catch (SQLException e) {
		}

		return conn;
	}

	private static Connection getConnectionForSubReport() {
		Connection conn = null;

		try {
			Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
		} catch (ClassNotFoundException e) {

		}
		// connection a la base de données testSubReports2
		// Cette connexion sera passée en paramètre et utilisée
		// par le sous-rapport

		try {
			String DBurl = "jdbc:odbc:testSubReports2";
			conn = DriverManager.getConnection(DBurl);
		} catch (SQLException e) {
		}

		return conn;
	}
}

A l'exécution de classe notre état est exporté dans un fichier pdf C:\Test2SubReport.pdf et affiché dans l'objet JasperViewer.

Image non disponible

IV. Téléchargement des sources

Le code source de l'application est disponible dans l'archive suivant :

Image non disponible Application et Jasper.rar.

Après décompression, vous aurez trois répertoires :

  • JASPER : Contient les fichiers Jasper/Jrxml, les fichiers jasper sont à mettre dans C:\JASPER.
  • testSubReports : Contient le code source de l'application, ainsi que les différents fichiers JAR nécessaires.
  • Les bases de données : Contient les deux bases de données ACCESS utilisées dans ce tutoriel.

V. Conclusion

Nous avons vu dans cet article comment utiliser des SubReports (sous-rapports) à travers un rapport master (parent) en utilisant l'api JasperReports. Le lecteur pourra élargir son étude en utilisant deux SGBD différents à la fois, il faudra juste faire attention aux types des champs de jointures qui doivent être de même type pour éviter de faire des conversions de types.

VI. Remerciements

Un grand merci à Fleur-Anne.Blain et Ricky81 pour leurs retours et remarques très précieuses.