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. |