MokaByte 105 - Marzo 2006
 
MokaByte 105 - Marzo 2006 Prima pagina Cerca Home Page

 

 

 

JasperReports
Una liberia Open Source per la reportistica - II parte

Secondo di tre articoli su JasperReports, una libreria Java Open Source che consente la generazione dinamica di report a partire da una fonte dati e la successiva renderizzazione in svariati formati, tra i quali PDF e HTML. Esempio pratico di utilizzo.

Introduzione
Nel precedente articolo è stata descritta l’architettura di JasperReports. In questo vedremo un esempio pratico di utilizzo delle API di tale libreria all’interno di un’applicazione Java. Per l’esempio faremo riferimento, come fonte dati, ad un database Access (per semplicità; ovviamente il db potrà essere di un vendor diverso da Microsoft) chiamato jasper e contenente una sola tabella, addresses avente cinque campi: id di tipo numerico, firstname, lastname, street e city di tipo testo.

 

Layout di un report
Ricordando lo schema di principio spiegato nell’articolo precedente (e riportato per comodità anche in questo in figura 1), la prima cosa da fare è quella di definire il layout dei report che dovrà generare l’applicazione.


Figura 1 - Processo di generazione di un report

Un layout non è altro che un file XML, con estensione .jrml, e costruito secondo lo schema definito in jasperreport.dtd.
In testa al layout troviamo la dichiarazione della versione dell’XML e l’indicazione del DTD adottato.

<?xml version="1.0"?>
<!DOCTYPE jasperReport PUBLIC "-//JasperReports//DTD Report Design//EN" "http://jasperreports.sourceforge.net/dtds/jasperreport.dtd" [
<!ENTITY reportFonts SYSTEM "./src/jr/test/ReportFonts.ent">
]>

Il tag root del file sarà <jasperReport>:

<jasperReport
name="GeneralTest"
pageWidth="595"
pageHeight="842"
columnWidth="515"
columnSpacing="0"
leftMargin="40"
rightMargin="40"
topMargin="50"
bottomMargin="50"
whenNoDataType="AllSectionsNoDetail"
isTitleNewPage="false"
isSummaryNewPage="false">

Gli attributi di questo tag sono gli attributi di pagina del report. Un report Jasper è in grado di accettare parametri in ingresso dalla classe Java preposta alla sua generazione. Il tag <parameter> serve a dichiarare tale parametri. L’attributo name indica il nome del parametro, mentre class ne indica il tipo Java. Nel nostro esempio i parametri in ingresso sono tre:

<parameter name="ReportTitle" class="java.lang.String"/>
<parameter name="FilterClause" class="java.lang.String"/>
<parameter name="OrderClause" class="java.lang.String"/>


Il tag <querystring> stabilisce la query SQL con la quale vengono recuperati dal database i dati che popoleranno il report.

<queryString><![CDATA[SELECT * FROM addresses]]></queryString>

Quindi, tramite il tag <field>, bisogna dichiarare i campi delle tabelle coinvolte e la loro tipologia Java:

<field name="Id" class="java.lang.Integer"/>
<field name="FirstName" class="java.lang.String"/>
<field name="LastName" class="java.lang.String"/>
<field name="Street" class="java.lang.String"/>
<field name="City" class="java.lang.String"/>

E’ possibile definire anche delle variabili tramite il tag <variable>:

<variable name="CityNumber" class="java.lang.Integer" resetType="Group" resetGroup="CityGroup" calculation="System">
<initialValueExpression>($V{CityNumber} != null)?(new Integer($V{CityNumber}.intValue()
                        
+ 1)):(new Integer(1))
</initialValueExpression>
</variable>

A questo punto possiamo definire le logiche di raggruppamento dei dati tramite il tag <group>:

<group name="CityGroup" minHeightToStartNewPage="60">
<groupExpression>
$F{City}
</groupExpression>
<groupHeader>
<band height="20">
<rectangle>
<reportElement x="0" y="4" width="515" height="15" forecolor="#C0C0C0" backcolor="#C0C0C0"/>
</rectangle>
<textField>
<reportElement x="0" y="4" width="515" height="15" backcolor="#C0C0C0" mode="Opaque"/>
<textElement textAlignment="Left">
<font reportFont="Arial_Bold"/>
</textElement>
<textFieldExpression class="java.lang.String">
" " + String.valueOf($V{CityNumber}) + ". " + String.valueOf($F{City})
</textFieldExpression>
</textField>
<line>
<reportElement x="0" y="19" width="515" height="0"/>
</line>
</band>
</groupHeader>
<groupFooter>
<band height="20">
<line>
<reportElement x="0" y="-1" width="515" height="0"/>
</line>
<staticText>
<reportElement x="400" y="0" width="60" height="15"/>
<textElement textAlignment="Right" lineSpacing="Single">
<font reportFont="Arial_Bold"/>
</textElement>
<text>Count : </text>
</staticText>
<textField>
<reportElement x="460" y="0" width="30" height="15"/>
<textElement textAlignment="Right" lineSpacing="Single">
<font reportFont="Arial_Bold"/>
</textElement>
<textFieldExpression class="java.lang.Integer">
$V{CityGroup_COUNT}
</textFieldExpression>
</textField>
</band>
</groupFooter>
</group>

Qualora volessimo uno sfondo personalizzato diverso da quello bianco di default, possiamo utilizzare il tag <background>. Nel nostro esempio aggiungeremo una immagine trasparente come sfondo per tutte le pagine:

<background>
<band height="742">
<image scaleImage="Clip" hAlign="Left" vAlign="Bottom">
<reportElement x="0" y="0" width="150" height="742"/>
<imageExpression>"src/jr/test/jr.watermark.gif"
</imageExpression>
</image>
</band>
</background>

Infine dobbiamo definire il layout delle regioni della pagina titolo, header, dettaglio, footer e sommario, rispettivamente tramite i tag <title>, <pageHeader>, <detail>, <pageFooter> e <summary>.

<title>
<band height="50">
<line>
<reportElement x="0" y="0" width="515" height="0" forecolor="black"/>
</line>
<textField isBlankWhenNull="true">
<reportElement x="0" y="10" width="515" height="30"/>
<textElement textAlignment="Center" lineSpacing="Single">
<font reportFont="Arial_Normal" size="22"/>
</textElement>
<textFieldExpression class="java.lang.String">$P{ReportTitle}</textFieldExpression>
</textField>
</band>
</title>

<pageHeader>
<band height="20">
<rectangle>
<reportElement x="0" y="5" width="515" height="15" forecolor="#333333" backcolor="#333333"/>
</rectangle>
<staticText>
<reportElement x="0" y="5" width="55" height="15" forecolor="white" backcolor="#333333" mode="Opaque"/>
<textElement textAlignment="Center">
<font reportFont="Arial_Bold"/>
</textElement>
<text>ID</text>
</staticText>
<staticText>
<reportElement x="55" y="5" width="205" height="15" forecolor="white" backcolor="#333333" mode="Opaque"/>
<textElement>
<font reportFont="Arial_Bold"/>
</textElement>
<text>Name</text>
</staticText>
<staticText>
<reportElement x="260" y="5" width="255" height="15" forecolor="white" backcolor="#333333" mode="Opaque"/>
<textElement>
<font reportFont="Arial_Bold"/>
</textElement>
<text>Street</text>
</staticText>
</band>
</pageHeader>

<detail>
<band height="20">
<textField>
<reportElement x="0" y="4" width="50" height="15"/>
<textElement textAlignment="Right"/>
<textFieldExpression class="java.lang.Integer">
$F{Id}
</textFieldExpression>
</textField>
<textField isStretchWithOverflow="true">
<reportElement x="55" y="4" width="200" height="15" positionType="Float"/>
<textFieldExpression class="java.lang.String">
$F{FirstName} + " " + $F{LastName}
</textFieldExpression>
</textField>
<textField isStretchWithOverflow="true">
<reportElement x="260" y="4" width="255" height="15" positionType="Float"/>
<textFieldExpression class="java.lang.String">
$F{Street}
</textFieldExpression>
</textField>
<line>
<reportElement x="0" y="19" width="515" height="0" forecolor="#808080" positionType="Float"/>
</line>
</band>
</detail>

<pageFooter>
<band height="40">
<line>
<reportElement x="0" y="10" width="515" height="0"/>
</line>
<textField>
<reportElement x="200" y="20" width="80" height="15"/>
<textElement textAlignment="Right"/>
<textFieldExpression class="java.lang.String">
"Page " + String.valueOf($V{PAGE_NUMBER}) + " of"
</textFieldExpression>
</textField>
<textField evaluationTime="Report">
<reportElement x="280" y="20" width="75" height="15"/>
<textElement textAlignment="Left"/>
<textFieldExpression class="java.lang.String">
" " + String.valueOf($V{PAGE_NUMBER})
</textFieldExpression>
</textField>
</band>
</pageFooter>

<summary>
<band height="60">
<textField isStretchWithOverflow="true">
<reportElement x="175" y="20" width="165" height="15"/>
<textElement textAlignment="Center">
<font reportFont="Arial_Italic"/>
</textElement>
<textFieldExpression class="java.lang.String">
"There were " +
String.valueOf($V{REPORT_COUNT}) +
" address records on this report."
</textFieldExpression>
</textField>
</band>
</summary>

 

Codice Java per la generazione di report
La classe Java del nostro esempio preposta alla generazione del report si chiama GeneralTest. Il metodo che esegue tutte le operazioni è createReport:

private int createReport( String[] pStrArgs )

Come parametro in ingresso riceve un array di stringhe. La prima operazione che viene eseguita è la creazione del semicompilato (file .jasper) secondo il template indicato come primo argomento dell’array pStrArgs:

// compilazione del report (creazione del .jasper a partire dal .jrxml)
try
{
JasperCompileManager.compileReportToFile( pStrArgs[0]);
}
catch( Exception jre )
{
System.out.println( "GeneralTest.createReport(): eccezione: " + jre.getMessage() );
return -1;
}

Il metodo compileReportToFile della classe JasperCompilerManager esegue la compilazione. A questo punto, ricordando la figura 2, abbiamo ottenuto il file .jasper da cui l’applicazione, in qualsiasi momento, sarà in grado di generare il report Nella nostra classe di esempio la generazione del report avviene subito dopo la creazione del semicompilato, ma nulla ovviamente vieta di eseguire l’operazione in un secondo momento. Per la generazione del report, il metodo fillReportToFile della classe JasperFillManager ha bisogno di una connessione verso il database da cui recuperare i dati. Il metodo getConnection della nostra classe di esempio crea la connessione:

private Connection getConnection() throws ClassNotFoundException, SQLException {
String lStrDriver = "sun.jdbc.odbc.JdbcOdbcDriver";
String lStrConnectString = "jdbc:odbc:Jasper";
String lStrUser = "";
String lStrPassword = "";

Class.forName( lStrDriver );
Connection lConn = DriverManager.getConnection( lStrConnectString, lStrUser, lStrPassword );
return lConn;
}

Il codice necessario per la creazione del report (file .jrprint) è il seguente:

// passaggio dei parametri al report
Map lParameters = new HashMap();
lParameters.put( "ReportTitle", "General test" );
lParameters.put( "FilterClause", “” );
lParameters.put("OrderClause", “Id” );

// creazione della connessione verso il database
Connection lConnection = getConnection();
// creazione del report (file .jrprint)
JasperFillManager.fillReportToFile( pStrArgs[1], lParameters, lConnection );

Il primo argomento del metodo fillReportToFile è il nome del file .jprint da creare.
Adesso dobbiamo decidere cosa fare del nostro report. Possiamo visualizzarlo, mandarlo in stampa o salvarlo su file in uno dei formati previsti da JasperReports. Nel nostro esempio viene salvato su un file PDF, grazie al metodo exportReportToPdfFile della classe JasperExportManager:

// export del report in PDF
JasperExportManager.exportReportToPdfFile( pStrArgs[2], pStrArgs[3] );

I due argomenti in ingresso al metodo exportReportToPdfFile sono, rispettivamente, il nome del report .jprint e il nome da assegnare al file PDF su cui viene salvato.
Il risultato finale sarà un report come quello mostrato in figura 2:


Figura 2 - Report generato tramite il codice di esempio

Conclusioni
In questo articolo abbiamo visto quali sono i passi comuni a tutte le operazioni di generazione di report tramite JasperReports e come viene realizzata la perfetta separazione del layout dai dati. Una volta definito un layout, la logica di generazione dei report diventa una operazione meccanica. Nel prossimo ed ultimo articolo vedremo come è possibile implementare un JRDataSource ad hoc per l’integrazione della libreria con una nostra applicazione e come rendere più agevole la creazione dei layout JRML mediante l’utilizzo di iReport, una applicazione Open Source basata su JasperReports.

Bibliografia
[1] Teodor Danciu - "Documentazione ufficiale di JasperReports", <http://jasperreports.sourceforge.net/>
[2] Wostmann & Associates - "JasperReports and iReports", http://www.wainet.com/Presentations/JasperReports%20&%20iReports.ppt <http://www.wainet.com/Presentations/JasperReports & iReports.ppt>

 

Risorse
L’esempio completo è allegato all’articolo.

Guglielmo Iozzia è laureato nel 1999 in Ingegneria Elettronica presso l’Università di Bologna. Ha realizzato, per la tesi di laurea, un software diagnostico per la predizione dell’andamento della pressione intracranica in pazienti in terapia intensiva neurochirurgica. Dall’inizio del 2000 si occupa, per conto di una azienda di Bologna del settore IT, dell’analisi e dello sviluppo di applicazioni Java 3-tier (Database, Application Server/Web Server, Client) in diversi ambiti (gestionale, mailbanking, industria meccanica, pubblica amministrazione). Dall’ autunno 2005 è anche responsabile delle architetture di rete per la stessa azienda.