52

j2007 04 jj5

Embed Size (px)

Citation preview

Page 1: j2007 04 jj5
Page 2: j2007 04 jj5
Page 3: j2007 04 jj5

EDITORIALEwww.infomedia.it

n.5 - novembre/febbraio 2008 3

JAVA Journal

BIMESTRALE - ANNO 2 - N.5

D I R E T T O R E R E S P O N S A B I L E MA R I A L E T I Z I A MA R I

E D I T O R I A L B O A R D

ST E F A N O SA N N A, MI C H E L E SC I A B A R R À , FA B I O ST A R O, TO N Y MO B I L Y

C O L L A B O R A T O R I

S T E F A N O S A N N AM I C H E L E S C I A B A R R ÀR I C C A R D O S O L I M E N A

FA B I O S T A R O

GR U P P O ED I T O R I A L E IN F O M E D I A S R L

V I A VA L D E R A P.11656038 PO N S A C C O (PI ) I T A L I A

TE L 0587736460 FA X 0587732232I N F O R M A Z I O N I@G R U P P O I N F O M E D I A . I T

W W W. I N F O M E D I A . I T

D I R E Z I O N E NA T A L E FI N O

C O N T A T T I

TE C H N I C A L B O O K(B O O K@G R U P P O I N F O M E D I A . I T )

M A R K E T I N G & A D V E R T I S I N GSE G R E T E R I A : 0587736460

(M A R KE T I N G@G R U P P O I N F O M E D I A . I T )

A M M I N I S T R A Z I O N E(A M M I N I S T R A Z I O N E@G R U P P O I N F O M E D I A . I T )

S E G R E T E R I A ( I N F O R M A Z I O N I@G R U P P O I N F O M E D I A . I T )

G R A F I C A(G R A F I C A@G R U P P O I N F O M E D I A . I T )

U F F I C I O A B B O N A M E N T ITE L 0587736460 FA X 0587732232

A B B O N A M E N T I@I N F O M E D I A . I T

W W W. I N F O M E D I A . I T

J A V A E T U T T I I M A R C H I D E R I V A T I S O N O M A R C H I O M A R C H I R E G I S T R A T I D I SU N M I C R O S Y S T E M S , I N C . N E G L I USA E I N A L T R I PA E S I . I L GR U P P O E D I T O R I A L E I N F O M E D I A È I N D I P E N -D E N T E D A SU N M I C R O S Y S T E M S , I N C .

J A V A J O U R N A L È U N A R I V I S T A D E L GR U P P O ED I T O R I A L E I N F O M E D I A S R LD I R E Z I O N E E A M M I N I S T R A Z I O N E :V IA VALDERA P.116 - PONSACCO (PI )RE G I S T R A Z I O N E P R E S S O I L TR I B U N A L E D I P I S A N .19 /06 D E L 5 /9 /2006

MySQL sotto l’ombrello di Sun Microsystems

Tra metà Gennaio e fine Febbraio, Sun Microsystems ha perfezionato l’acquisizione di MySQL AB, il noto produttore del diffuso DBMS dual-license MySQL, con un investimento di circa 1 miliardo di dollari.

Con questa acquisizione, MySQL AB è a tutti gli effetti una business unit di Sun Microsystems, e potrà avvalersi del peso di Sun per far crescere ulteriormente il prodotto, in termini di marketing e di servizi.

Sun, invece, oltre ad ampliare il portfolio dei propri prodotti, si posiziona giusto dietro la “M” dell’acronimo LAMP, una posizione decisamente centrale nel mondo del software open source. E sul Nasdaq il valore del titolo Sun (il cui simbolo, guarda caso, è JAVA) giusto in concomitanza dell’intera operazione ha avuto un rialzo.

Se da un punto di vista del licensing e di ciò che riguarda la programmazione con MySQL non ci sono novità di sorta, ciò che verrà potenziato è il supporto tecnico, che Sun offre in modalità 24x7 per l’intero portfolio di prodotti MySQL.

In sintesi un robusto supporto commerciale per un prodotto software open source. Indispensabile in ambienti con applicazioni mission-critical basate su MySQL.

Oltre al supporto tecnico, l’acquisizione potrà far convergere più speditamente MySQL verso importanti tappe per applicazioni di fascia alta (dallo sviluppo di nuovi engine, al supporto multicore spinto, all’interoperabilità, alla sincronizzazione distribuita dei dati, e via dicendo).

Per quanto riguarda Java in particolare, ci auguriamo che la maggiore affinità tra Sun, MySQL AB e le relative community permetta di sviluppare sempre più strumenti evoluti e tecnologie per poter far interagire in modo ancor più efficiente e ottimizzato le applicazioni Java con basi di dati basate (anche) su MySQL.

Con questo passo Sun (che già supporta direttamente PostgresSQL) si propone sempre più come player nel mondo data center, e MySQL (con 55.000 download al giorno) è un’importante pedina per consolidare ulteriormente questa posizione.

La redazione di Java Journal

Page 4: j2007 04 jj5

solo49,00 Euro

“Architetture EnterpriseMetodologie di disegno e progettazione”di Alessandro Sinibaldi

Anno 2008

350 pagine, formato 17x24 cm

ISBN 9788881500352

Ordinalo su

www.shopinfomedia.com

Se siete tra quelle persone a cui è sempre piaciuto smontare le cose per vedere come sono fa�e, allora questo libro è per voi. Fare gli archite�, di qualsiasi campo si tra�, vuol dire cercare di coniugare due aspe�: uno tecnico e uno crea�vo. Quello tecnico, perché l’archite�o pensa alla stru�ura delle cose, a come sono composte da par� più piccole e a come queste interagiscono fra loro per far muovere un corpo com-plesso. Tu�o questo mi è sempre sembrato affascinante. L’aspe�o crea�vo è dovuto al fa�o che un archite�o, quando è mol� anni che fa il suo lavoro, sviluppa una specie di senso este�co di quello che fa. Vedere le cose muoversi in armonia ed evolvere è sicuramente una grossa soddisfazione. L’importante è capire i principi fondamentali del funzionamento, poi, i pezzi possono essere assembla� in miliardi di modi diversi e dar vita ad un numero, ancora maggiore, di situazioni e stru�ure diverse. Lo stesso accade ad un archite�o IT.

“Architetture Enterprise”

Page 5: j2007 04 jj5

Speciale JBoss SeamIntervista a Fabio Staro su JBoss Seamdi Michele Sciabarrà 9JBoss Seam: un framework per il Web 2.0di Fabio Staro 11

EducationalDAO Generator di Riccardo Solimena 23

Focus JPA: JAVA Persistence API - Seconda partedi Fabio Staro 38

Rubriche

Community WatchIntervista al JAVA User Group Torino

di Stefano Sanna 46

SOMMARIO Novembre/Febbraio 2008

numero 5 JAVA Journal

n.5 novembre/febbraio 2008

Page 6: j2007 04 jj5

IN VETRINA

6 JAVA JOURNAL > n.5 novembre/febbraio 2008

JavaFX Script: Dynamic Java Scripting for Rich Internet/Client-side Applicationsdi J. WeaverApress216 pp - euro 38,90 ISBN 9781590599457

Java Pocket Guide Cookbook, Second Edition di R. Liguori e P. LiguoriO’ Reilly191 pp - euro 20,95ISBN 9780596514198

Programming with Alice and Java. International Editiondi J. Lewis e P. DePasqualeAddison Wesley360 pp - euro 75,95ISBN 9780321549358

Core Servlets and JavaServer Pages, Volume 2 - Advanced Technologies 2nd Editiondi M. Hall, L. Brown e Y. ChaikinAddison Wesley736 pp - euro 44,95ISBN 9780131482609

Java Software Solutions. Foundations of Program Design: International Edition (VI)di J. Lewis e W. LoftusAddison Wesley832 pp - euro 74,95ISBN 9780321549341

Pro NetBeans IDE 6: Rich Client Platform Editiondi A. MyattApress491 pp - euro 55,00ISBN 9781590598955

Value Pack: Absolute Java (Int Ed) with Practical Debugging in Javadi W. Savitch e Ann Ford Addison WesleyValue pack - euro 83,95 ISBN 9781405825054

The Ruby Programming Languagedi D. Flanagan e Y. MatsumotoO’ Reilly 444 pp - 39,00 euroISBN 9780596516178

AJAX, RIA and Web Developmentdi di P. Deitel e H. DeitelPrentice Hall1040 pp - 52,95 euroISBN 9780131587380

Page 7: j2007 04 jj5

IN VETRINA JAVA JOURNAL 5

Scrivi a [email protected] specificando nell’oggetto della e-mail: IN VETRINA Java Journal n. 5

oppure inviaci il coupon al numero di fax 0587/732232

Potrai acquistare i libri qui riportati con uno SCONTO ECCEZIONALE del 10% anche se acquisti solo un libro

oppure del 20% se acquisti 3 libri

Apache. Codice e comandi essenzialidi D. Lopez - J. BlancoPearson Education Italia240 pp - euro 12,00ISBN 9788871924021

UML Distilleddi M. FowlerPearson Education Italia162 pp - euro 25,00ISBN 9788871922072

Web: http://book.infomedia.it • E-mail: [email protected] Tel: 0587-736460 • Fax: 0587-732232

DATI

ANAG

RAFI

CI

NOME COGNOME CODICE CLIENTE

INDIRIZZO

TELEFONO FAX E-MAIL

DITTA CODICE FISCALE

INTESTAZIONE FATTURA P.IVA

Garanzia di riservatezza - Il Gruppo Editoriale Infomedia garantisce la massima riservatezza dei dati da Lei forniti e la possibilità di richiederne gratuitamente la rettifica o la cancellazione scrivendo a: Responsabile Dati - Gruppo Editoriale Infomedia Srl, V. Valdera P. 116, 56038 PONSACCO (PI). Le informazioni custodite nel nostro archivio verranno utilizzate al solo scopo di inviarle proposte commerciali, in conformità alla legge 196/03 sulla tutela dei dati personali.

ORDI

NE

TITOLO CODICE ISBN PREZZO QUANTITA’

Le spese di spedizione sono soggette a variazioni in base alle tariffe postali. Per i pagamenti con assegno, c/c postale e bonifico bancario consigliamo di chiedere conferma in redazione oppure consultare il sito Internet.

PER POSTA: + 3,00 Euro A MEZZO CORRIERE: + 7,00 Euro

ALLEGO ASSEGNO BANCARIO INTESTATO A “GRUPPO EDITORIALE INFOMEDIA SRL” - NON TRASFERIBILE

CONTRASSEGNO (solo per spedizione a mezzo Posta): + 8,00 Euro

ALLEGO VERSAMENTO SU C/C POSTALE N.14291561 INTESTATO A “GRUPPO EDITORIALE INFOMEDIA SRL - PONSACCO” - CODICE IBAN: IT84X0760114000000014291561 - Indicare la causale del versamento

ALLEGO FOTOCOPIA DEL BONIFICO BANCARIO SU C/C 000000014804 CAB 25200 ABI 01030 CIN “A” - MONTE DEI PASCHI DI SIENA, AGENZIA DI PERIGNANO - CODICE IBAN: IT84A0103025200000000014804

CARTA DI CREDITO VISA/MASTERCARD/CARTASI’ N. SCADENZA CODICE DI SICUREZZA “CV2”

TITOLARE _______________________________________________

NATO IL FIRMA _______________________________________________________SPED

IZIO

NE

E PA

GAM

ENTO

SI, mi voglio iscrivere gratuitamente alla newsletter Infomedia che mi tiene aggiornato sui prossimi argomenti delle mie riviste di programmazione e mi informa delle offerte, le nuove uscite e le prossime pubblicazioni. Posso annullare la mia iscrizione in qualsiasi momento alla pagina: www.infomedia.it/newsletter.php. Questo è il mio indirizzo di posta elettronica: ______________________________________________

CAP CITTA’ PROV.

The Art & Science of Javascriptdi C. AdamsSitepoint258 pp - euro 43,50ISBN 9780980285840

Page 8: j2007 04 jj5
Page 9: j2007 04 jj5

JAVA Journal

9n.5 - novembre/febbraio 2008

JBOSS Seam speciale

>> di Michele Sciabarrà ([email protected])

Ciao Fabio, per prima cosa una breve presenta-zione. Ci parli un attimo di te?

Lavoro presso la Direzione Ricerca & Innovazione di Engineering Ingegneria Informatica S.p.A. Nella direzione vi sono vari centri di eccellenza (in particolare per le piattaforme JEE ed Oracle oltre ad un centro di competenza specifico sui temi dell’Ac-cessibilità).Presso la direzione sono responsabile tecnico per i progetti Java. Il progetto che seguo attualmente è per un cliente istituzionale dove stiamo usando tecnolo-gie all’avanguardia(JSF e Shale-Clay con il vincolo dell’accessibilità…).

So che ti occupi di JBoss Seam. Ci spieghi bre-vemente cos’è questo framework, prima di la-sciare i lettori di Java Journal alla lettura dell’ar-

ticolo di approfondimento su Seam?

JBoss Seam è un application framework per Java En-terprise Edition versione 5 (JEE 5). Il framework for-nisce un modello a componenti uniforme dove non vi è una distinzione tra i componenti del presentation tier ed i componenti della business logic.

Nella versione 5 della Enterprise Edition di Java il framework Java Server Faces (JSF) e la tecnologia EJB 3.0 costituiscono le feature principali e maggior-mente innovative. JSF è un framework per il presen-tation tier basato su un modello di componenti e di eventi attraverso i quali sono descritti gli elementi della user interface e l’interazione con l’utente; vice-versa la tecnologia EJB 3.0 consente la programma-zione di componenti server side dedicati alla business lo-gic e alla persistenza dei dati. Le due tecnologie sono eccellenti nei “relativi tier” e sono necessariamente complementari in una complessa applicazione di li-vello enterprise. Tuttavia, nella specifica JEE 5, non è presente uno standard per integrare i due mondi.

E quindi cosa succede in JBoss Seam?

Seam risolve questo limite unificando i modelli a componente di JSF e degli EJB 3.0 eliminando di fat-to il codice di integrazione, il cosiddetto “glue code”, tra le tecnologie. Tanto per essere chiari, in Seam un action listener JSF può essere, ed anzi lo è solitamente,

Intervista a Fabio Staro su JBoss Seam

JBOSS SEAM è un application framework per

Java Enterprise Edition

Page 10: j2007 04 jj5

JAVA Journal

n.5 - novembre/febbraio 200810

speciale JBOSS Seam

un EJB di tipo session …

Ma non va contro alcune delle best practice?

Si, quanto precedentemente affermato è sostanzialmente in contrasto con il principio, presente nelle principali ar-chitetture J2EE, di “accoppiamento debole” tra i diversi tier applicativi. Tuttavia gli EJB 3.0 hanno rivoluzionato la natura dei componenti EJB. Se prima un componente EJB versione 2.x era un “corse-grained heavyweight object” ora un EJB 3.0 è diventato un “fine-grained object”, non più complesso di un normale bean java con la presenza di annotazioni.

Come fa allora Seam a implementare questa “unifica-zione”?

I concetti fondamentali in Seam sono le nozioni di contesto e di componente. I componenti sono oggetti stateful, solitamente EJB 3.0 ma anche semplici POJO, presenti in un contesto. Seam estende il “context model” definito nella specifica delle Servlet Java ed aggiunge ai noti contesti della request, del-la session e della application i contesti conversation e bu-siness process.

Cos’è una conversation in Seam?

Una “conversazione” in Seam è una unità di lavoro dal punto di vista dell’utente costruita sulle diverse interazio-ni che l’utente ha con il sistema allo scopo di risolvere un problema specifico come per esempio la prenotazione di un volo o la registrazione del profilo in una anagrafica. Per-tanto una “conversazione” è costituita da varie richieste HTTP e da varie transazioni sul database.

E invece il business process in Seam?

Il contesto di business process è definito nei processi di

business di tipo “long running” dove lo stato è gestito e reso persistente da un motore di BPM (business process management). Una business process interaction può coin-volgere più utenti e lo stato è condiviso tra questi in un modo ben definito.

In cosa si differenzia da SpringFramework?

La differenza principale tra l’application framework JBoss Seam e Spring è che il primo è realizzato “specificatamen-te” per la versione 5 della enterprise edition di Java dove Java Server Faces e gli EJB 3.0 costituiscono le tecnolo-gie di riferimento (sebbene già con Seam versione 1.1 era possibile installare applicazioni Seam che usano semplici POJO su application server J2EE 1.4).

Un componente Seam è un POJO, acronimo di “Plain Old Java Objects”, e quindi può essere un semplice bean java o un EJB 3.0. Tuttavia Seam è stato progettato avendo pre-sente la nuova specifica per gli EJB offrendo di conseguen-za una completa integrazione tra la tecnologia EJB e JSF.

Seam presenta una serie di “feature” innovative. A titolo di esempio: in Seam oltre ai concetti di Inversion of Con-trol (IoC) e di dependency injection, concetti di cui Spring è principale portavoce, introduce il concetto di Bijection.

Le caratteristiche che contraddistinguono la Bijection ri-spetto all’IoC sono la:

§ contestualità: la bijection consente di assemblare componenti presenti nei differenti contesti;

§ bidirezionalità: i valori delle variabili di contesto sono injected negli attributi di un componente e sono outjected dagli attributi del componente in-dietro nel contesto consentendo al componente di manipolare il valore delle variabili di contesto semplicemente impostando le proprie variabili di istanza.

§ dinamicità: la bijection avviene ogni volta che un componente è invocato.

Ha funzionalità AJAX?

Sì. Seam fornisce un “built-in JavaScript remoting layer” per invocare i componenti EJB. I client AJAX possono invocare componenti server side facilmente e senza la necessita di un action layer intermedio. Seam 1.1 integra soluzioni open source JSF-AJAX based come ICEfaces e Ajax4JSF.

Una conversazione in Seam è una unità di

lavoro dal punto di vista dell’utente

Note Biografiche

Michele Sciabarrà ha scritto di Java e lo ha insegnato per dieci anni. Quando non cerca di imparare le ultime API o disquisisce della superiorità del modello Open Source, si diletta a program-mare cellulari Symbian in C++ e amministrare sistemi Linux.

Page 11: j2007 04 jj5

JAVA Journal

11n.5 - novembre/febbraio 2008

JBOSS Seam speciale

JBoss Seam è un “deep integration” framework open source nato con l’obiettivo di integrare le tecnologie Java EE e semplificare lo sviluppo. È un framework per il Web 2.0 che offre soluzioni native per AJAX, per il remoting, per la gestione del workflow e per il business process management.

>> di Fabio Staro ([email protected])

Nella Java Enterprise Edition ver-sione 5.0 sono presenti le tecnolo-gie Java Server Faces (JSF) ed En-terprise Java Beans (EJB) versione 3.0. JSF è il framework di riferimen-to per realizzare applicazioni web in

Java, quindi il presentation layer; viceversa, la tecnolo-gia EJB è lo standard con il quale implementare la lo-gica di business di una applicazione, quindi il business layer. Le due tecnologie sono eccellenti nei “relativi tier” e sono necessariamente complementari in una complessa applicazione di livello enterprise. Tuttavia, nella specifica JEE 5, non è presente uno standard per integrare i due mondi.

Integrazione tra JSF e EJB

Il modello a componenti presente in JSF non si in-tegra in modo naturale con il modello a componenti

degli EJB. Per far sì che alla pressione di un pulsante di submit (presente in un form HTML) venga invoca-to un metodo di business (presente in un componen-te EJB) è necessario dichiarare ed implementare un bean JSF; il quale ha un’unica funzione: fungere da ponte verso la tecnologia EJB. Quindi, per integrare i framework JSF ed EJB è necessario “passare” per i backing bean che costituiscono un artificial layer che nulla ha a che fare con le reali esigenze del business (cfr. [JPH]). Inoltre, per ottimizzare le operazioni di “ricerca” e le modalità di invocazione dei componenti EJB, le “tra-dizionali” applicazioni Java EE fanno uso di vari desi-gn pattern come il service locator, il business delegate ed il business facade. Si può argomentare (e tutto sommato in questi anni varie best practice lo hanno evidenziato) che la separazione netta tra il presetation layer ed il bu-siness layer porta in dote un debole accoppiamento tra la view ed il business; e questo è un beneficio quando la logica di business è acceduta da diversi front-end. Nella realtà, tuttavia, ciò accade raramente. Di solito le applicazioni web fanno un uso esclusivo dei com-ponenti EJB (in genere ad interfaccia locale) e l’ac-coppiamento tra la view ed il business è tale che una modifica su un layer si riflette sull’altro.

JBoss Seam

JBoss Seam, ideato da Gavin King, l’inventore di Hi-bernate ([3]), propone un approccio pratico, foriero di benefici e di semplificazioni nello sviluppo. Non ci obbliga a non usare i design pattern precedentemen-te citati, tuttavia il framework, fornendo un modello

JBoss Seam: un framework per il Web 2.0

JBoss Seamè stato ideato da

Gavin King, l’ideatore di Hibernate

Page 12: j2007 04 jj5

JAVA Journal

n.5 - novembre/febbraio 200812

speciale JBOSS Seam JAVA Journal

n.5 - novembre/febbraio 2008 13

JBOSS Seam speciale

a componenti uniforme (dove non vi è una distinzione tra i componenti del presentation ed i componenti della business logic), rende superfluo il codice di integrazione (glue code) tra le diverse tecnologie. Il modello a componenti proposto da JBoss Seam è talmente innovativo che presso la Java Community è in itere un processo di standardizzazione (JSR 299, cfr. [5]).Ma JBoss Seam non è “solo” un framework di integrazio-ne. È anche un framework per il Web 2.0. Per prima cosa estende i contesti di sessione e di applicazione, tipici in una applicazine web, introducendo i contesti di conversa-tion e di business process, più utili per la definizione di un flusso applicativo:

§ conversation context descrive in modo naturale un caso d’uso che si realizza da parte di un utente at-traverso varie interazioni http;

§ business process context, consente di trattare i pro-cessi di business di tipo “long running” dove i task di business sono realizzati anche da diversi utenti nel tempo.

Secondariamente, il framework integra in modo naturale soluzioni per il remoting e AJAX. Prima di tutto fornisce un built-in JavaScript remoting layer con il quale i client AJAX possono invocare i componenti server side (quindi EJB) direttamente e senza la necessità di un action layer inter-medio; inoltre integra soluzioni open source JSF-AJAX ba-sed come Ajax4JSF.

Infine, JBoss Seam è un framework progettato per i mo-tori ORM (Object Relational Mapping) e quindi per inte-grare la specifica JPA (cfr. [JJ3]). Una delle principali fea-

ture in un framework ORM è il supporto ad una politica di caricamento del tipo lazy con la quale le entità in relazione vengono caricate in memoria solo quando è strettamen-te necessario (cfr. [JJ3]). Tuttavia una politica di carica-mento di tipo lazy non è semplice da realizzare in un fra-mework tradizionale MVC (Model View Controller), e per superare tale limitazione sono nati vari design pattern, tra i quali il più noto è il pattern Open Session In View ([6]). Per i necessari approfondimenti rimandiamo alla letteratura sul tema; tuttavia è d’obbligo evidenziare che JBoss Seam supporta il pattern Open Session In View come default. Per-tanto, in una applicazione Seam è bandita l’eccezione di tipo LazyInitializationException e le prestazioni del motore ORM sono decisamente migliorate poiché il persistence con-text è di tipo esteso ed agisce come cache naturale dei dati, riducendo il round trip verso il database (cfr. [JJ3], [JPH]).Come si può intuire da quanto finora detto, l’articolo pre-suppone una conoscenza del mondo JEE ed in particolare del framework JSF e della tecnologia EJB versione 3.0 (ri-

FIGURA 1 Installazione dell’application server JBoss con il profilo EJB 3.0

JBoss Seamè anche

un framework per il Web 2.0

Page 13: j2007 04 jj5

JAVA Journal

n.5 - novembre/febbraio 200812

speciale JBOSS Seam JAVA Journal

n.5 - novembre/febbraio 2008 13

JBOSS Seam speciale

mandiamo alla bibliografia e ai numeri 1 e 3 di JavaJour-nal per gli opportuni approfondimenti). Per inciso, l’esempio riportato è stato verificato con l’ap-plication server JBoss versione 4.0.5 (JEMS Installer 1.2.0 [4], installato con l’opzione EJB 3.0, Figura 1) e con il da-tabase MySQL versione 5.0.27 [2]. La versione del fra-mework JBoss Seam usata è la 1.2.1 [7].

Un primo esempio

L’esempio che desideriamo realizzare è volutamente sem-plice al fine di introdurre alcune delle “feature” del fra-mework eliminando le complessità applicative legate ad un particolare dominio di interesse. L’applicazione consen-te l’inserimento in tabella dei dati anagrafici di un utente e consiste di:

§ una pagina jspx (editUtente.jspx) attraverso cui in-serire i dati anagrafici;

§ un Session Stateless EJB (Rubrica.java), compo-nente che espone il servizio di salvataggio dei dati e che interagisce con la API JPA dell’Entity Manager;

§ un entity bean (Utente.java), che è il data model dell’esempio (Figura 2).

Di seguito riportiamo uno stralcio del codice della classe Utente.

@Entity(name=”utente”)@Name(“utente”)@Scope(SESSION)@Table(name=”UTENTE”)public class Utente implements Serializable{ ... private String nome; private String cognome; private String telefono; private String indirizzo;

@Id public String getCognome() { return cognome; } ...}

Osserviamo nel dettaglio il codice riportato. Le annotazio-ni @Entity, @Table e @Id sono annotazioni della specifi-ca JPA e caratterizzano la classe Utente come entity bean. In particolare:

§ L’annotazione @Entity informa il provider dei servizi JPA che la classe è di fatto una entità per la quale deve gestire la persistenza;

FIGURA 2 La semplice applicazione per il salvataggio in rubrica

Page 14: j2007 04 jj5

JAVA Journal

n.5 - novembre/febbraio 200814

speciale JBOSS Seam JAVA Journal

n.5 - novembre/febbraio 2008 15

JBOSS Seam speciale

§ L’annotazione @Table specifica la tabella di map-ping;

§ L’annotazione @Id identifica la chiave primaria della entità (cfr. [JJ3]).

Le annotazioni @Name e @Scope sono, invece, del fra-mework Seam. Analizziamole in dettaglio. L’annotazio-ne @Name, presente nel package org.jboss.seam.annotations, consente di identificare la classe Utente come componen-te Seam il cui nome è “utente”. Pertanto la classe Uten-te è contemporaneamente sia un entity bean (annotazio-ne @Entity della specifica JPA) sia un componente Seam (annotazione @Name). Un componente Seam è un POJO (acronimo di Plain Old Java Object), il cui tipo può essere tra quelli riportati nell’elenco che segue:

§ EJB 3.0 Session Stateless;§ EJB 3.0 Session Stateful;§ EJB 3.0 Entity Bean;§ JavaBean;§ EJB 3.0 Message Driven Bean.

Osservando l’elenco è possibile notare come Seam sia stato progettato e disegnato specificatamente per gli EJB 3.0 e che tuttavia il framework può anche funzionare in assenza di EJB 3.0, definendo come componenti dei “semplici” Ja-vaBean. Tuttavia l’uso di EJB 3.0 come componenti Seam porta in dote tutti quei servizi propri della specifica degli Enterprise Java Beans (cfr [JJ2a] e [JJ2b]) e di un contai-ner EJB, tra i quali i principali sono:

§ Gestione da parte del container delle transazio-ni;

§ Gestione da parte del container della sicurezza;§ Injection dell’Entity Manager attraverso l’anno-

tazione @PersistenceContext;§ Definizione dei componenti EJB come WebSer-

vice (attraverso l’annotazione @WebService, cfr. [JJ2b]);

§ Supporto per i componenti asincroni (i message driven bean).

Torniamo ad analizzare la classe Utente. Il valore specificato con l’annotazione @Name, ossia il nome del componente

Seam, deve essere univoco in tutta l’applicazione. Un com-ponente Seam, e quindi nel nostro caso il componente di nome “utente”, può essere direttamente referenziato dai componenti JSF presenti nelle pagina jsp.Per esempio, la seguente riga di codice, estratta dalla pagi-na editUtente.jspx, collega il valore di un campo di tipo testo al nome dell’utente:

<h:inputText id=”txt_nome” value=”#{utente.nome}” required=”true”/>

La cosa interessante è che “utente” è un componente Seam e non un “semplice” backing bean JSF; inoltre, os-serviamo che “utente” non è stato dichiarato (come invece è norma in una applicazione JSF) nel file di configurazione faces-config.xml (cfr. [JJ1]). Riprendiamo l’analisi del codice della classe Utente. L’annotazione @Scope, presente nel package org.jboss.seam.annotations, consente di specificare il contesto nel quale vive ed è definito un componente Seam. Il framework definisce e gestisce i seguenti contesti:

§ Stateless;§ Event (o request);§ Page;§ Conversation;§ Session;§ Business process;§ Application.

I contesti, ad eccezione del contesto Stateless, sono in rela-zione gerarchica tra loro (Figura 3).

FIGURA 3 I contesti in SeamSeam

può anche funzionare in assenza di

EJB 3.0

Page 15: j2007 04 jj5

JAVA Journal

n.5 - novembre/febbraio 200814

speciale JBOSS Seam JAVA Journal

n.5 - novembre/febbraio 2008 15

JBOSS Seam speciale

Nell’elenco riportato possiamo notare come ai tre contesti tipici di una applicazione web (ossia i contesti di request, session ed application), il framework aggiunga i contesti di conversation e business process. In particolare, il conversation context è un concetto centrale nell’architettura Seam. Una conversazione è una unità di lavoro dal punto di vista dell’utente, costruita sulle diver-se interazioni che l’utente ha con il sistema allo scopo di risolvere un problema specifico (ad esempio la prenotazio-ne di un volo o la registrazione di un profilo utente in una

anagrafica). Pertanto, una conversazione è costituita da varie richieste HTTP e da va-rie transazioni sul database. Il framework Seam offre due tipi di conversazioni:

§ Temporary conversation;§ Long running conversation.

Una temporary conversation esiste per la du-rata di una request, incluse eventuali ope-razione di redirect. Questa “feature” risol-ve un limite presente nell’architettura JSF, dove normalmente una operazione di redi-rect non conserva le informazioni salvate nel FacesContext (ad esempio le istanze della classe FacesMessages). Viceversa, una long running conversation esi-ste per la durata di una ben definita serie di request http; i confini della conversazio-ne sono definiti o mediante file di configu-razione o mediante annotazioni. È impor-tante sottolineare che vi possono essere più conversazioni attive e concorrenti durante una sessione HTTP; ma ogni conversazio-ne, con le sue variabili di contesto, è isola-ta dalle altre. Il contesto di business process è definito nei processi di business di tipo “long running” dove lo stato è gestito e reso persistente da un motore di BPM (Business Process Ma-nagement) come JBoss jBPM. Una business process interaction può coinvolgere più utenti e lo stato è condiviso tra questi in un modo ben definito. La definizione di un processo di business sì complesso è possibile attra-verso un process definition language.

Riprendiamo l’analisi del codice analizzan-do la classe Rubrica (cfr. Listato 1). Que-sto componente è un Session Stateless EJB (annotazione @Stateless). Osservando il codice riportato nel Listato 1 possiamo osservare che sono presenti le annotazioni del framework Seam: @Name, @Logger e @In. In particolare, il valore dell’annota-zione @Name è “rubrica”. Come nel caso del componente “utente” anche il com-ponente Seam “rubrica” può essere diret-tamente referenziato dai componenti JSF presenti in una pagina jsp. Pertanto il me-

todo di business salva() è direttamente referenziato (sen-za la mediazione di backing bean) come action dal command button JSF presente nella pagina editUtente.jspx:

<h:commandButton value=”Salva” action=”#{rubrica.salva}”/>

Il metodo memorizza in tabella i valori di un utente. Ma chi “passa” l’utente da memorizzare all’EJB Rubrica? Os-servando il codice della classe Rubrica è intuibile come l’annotazione @In sia responsabile della valorizzazione

LISTATO 1 Il codice del Session Stateless EJB: Rubrica.java

package it.example.action;

import …;

@Stateless @Name(“rubrica”)public class Rubrica implements RubricaLocal{

@In private Utente utente; @PersistenceContext private EntityManager em; @Logger private static Log log; public String salva() { List existing = em.createQuery( “select u.cognome from utente u where u.cognome= #{utente.cognome}”).getResultList(); if ( existing.size()==0 ) { em.persist(utente); FacesMessages.instance().add( “Registrato utente: #{utente.cognome} #{utente.nome} nella rubrica”);

log.info(“Registrato utente: #{utente.cognome} #{utente.nome} nella rubrica”); return null; } else { FacesMessages.instance().add( “Utente #{utente.cognome} già presente in rubrica.”); return null; } }}

Page 16: j2007 04 jj5

JAVA Journal

n.5 - novembre/febbraio 200816

speciale JBOSS Seam JAVA Journal

n.5 - novembre/febbraio 2008 17

JBOSS Seam speciale

dell’attributo utente:

@Inprivate Utente utente;

Siamo in presenza di un processo di dependency injection (cfr. [JJ2a]). L’infrastruttura di Seam legge il nome del-la variabile di istanza, “utente” nel nostro caso, ed inizia a cercare una variabile con lo stesso nome nei vari contesti (iniziando la ricerca dal contesto Event e proseguendo nei contesti via via più ampi). Prima di invocare il metodo di business salva(), il framework valorizza l’attributo utente della classe Rubrica con il valore della variabile recuperato dalla sessione.

Altro esempio di dependency injection è l’annotazione @Log-ger:

@Loggerprivate static Log log;

Attraverso questa annotazione l’infrastruttura Seam iniet-ta e valorizza l’attributo “log” prima che sia invocato un qualsiasi metodo di business. Prima di concludere que-sta introduzione all’injection presente in Seam è doveroso notare come in Seam esista anche il concetto di bijection esemplificato dall’annotazione @Out (simmetrica alla an-notazione @In). In sintesi, il valore delle variabili di conte-sto è injected dal contesto negli attributi di un componente attraverso l’annotazione @In, ed è outjected dagli attributi del componente indietro nel contesto attraverso l’annota-zione @Out, consentendo di fatto al componente di ma-nipolare il valore delle variabili di contesto semplicemente impostando le proprie variabili di istanza. Prima di concludere l’analisi del codice della classe Rubri-ca osserviamo che è presente l’annotazione @Persistence-Context:

@PersistenceContextprivate EntityManager em;

Attraverso questa annotazione, esempio di dependency injec-

tion (in questo caso da parte del container EJB), è valoriz-zato l’Entity Manager JPA (cfr. [JJ3]).

Validazione via Hibernate

Nelle applicazioni web la vali-dazione dei dati di input deve essere eseguita non solo nel presentaion layer ma anche nel business layer e tipicamente pri-ma del salvataggio dei dati. Generalmente un framework web, ad esempio JSF, ed un framework ORM, ad esempio JPA, hanno meccanismi dif-ferenti e tutto sommato ridon-

danti per la validazione dei dati. Seam consente di definire le regole di validazione sui dati attraverso annotazioni in-serite direttamente sugli entity bean JPA. Questi constrain per la validazione sono usati automaticamente nella vali-dazione dei campi di input di un form HTML quando l’en-tity bean è usato come backing bean JSF. In questo modo il codice della pagina web (nel nostro caso editUtente.jspx) ri-sulta privo dei validatori del framework JSF (cfr. Listato 2). Le annotazioni che definiscono le regole sui dati sono parte del framework Hibernate Validator, perfettamente integrato in Seam. Di seguito riportiamo uno stralcio del codice della classe Utente con le annotazioni di validazione:

@Length(min=3, max=50, message = “La lunghezza deve essere compresa tra {min} e {max} caratteri.”)public String getIndirizzo(){ return indirizzo;}

L’annotazione @Length, presente nel package org.hibernate.validator, consente di definire un constrain sul-la lunghezza del campo. Alcune tra le annotazioni presenti nel framework Hibernate Validator sono @NotNull, @Ran-ge e @Pattern. Con quest’ultima è possibile usare un’espres-sione regolare per definire la regola di validazione.

Per integrare Hibernate Validator in una pagina jsp, Seam offre i tag validateAll e decorate. Il tag validateAll consente di “identificare” i componenti JSF per i quali applicare le re-gole di validazione, mentre il tag decorator consente di ge-stire la visualizzazione dei messaggi di errore “wrappan-do” i facet JSF beforeInvalidField e afterInvalidField in presen-za di un errore. Di seguito è riportato uno stralcio del codi-ce della pagina editUtente.jspx: …<f:facet name=”beforeInvalidField”> <h:outputText value=”X” styleClass=”erroreInput” />

FIGURA 4 La pagina editUtente.jspx con i controlli di validazione

Page 17: j2007 04 jj5

JAVA Journal

n.5 - novembre/febbraio 200816

speciale JBOSS Seam JAVA Journal

n.5 - novembre/febbraio 2008 17

JBOSS Seam speciale

</f:facet> <f:facet name=”afterInvalidField”> <s:message styleClass=”erroreInput” /></f:facet><s:validateAll> … <tr> <td>Nome*</td> <td> <s:decorate> <h:inputText value=”#{utente.nome}” required=”true”/> </s:decorate> </td> </tr> …</s:validateAll>…

Osservando il codice precedente possiamo notare che, in presenza di un errore di validazione, prima del campo di input è visualizzato il carattere “X”, dichiarato nel facet JSF beforeInvalidField; e che dopo il campo di input è visualizza-to il messaggio di errore vero e proprio, facet JSF afterInva-lidField (Figura 4).

Configurazione e packaging dell’applicazione

L’applicazione di esempio è una applicazione enterprise costituita da un modulo web e da un modulo EJB (Figu-ra 5). I file di configurazione dell’archivio enterprise rubri-ca_EAR.ear sono presenti all’interno della directory META-INF e sono:

§ application.xml;§ jboss-app.xml

Il codice del file application.xml, specifico di una applicazio-ne enterprise Java, è riportato nel Listato 3; viceversa, il file jboss-app.xml, come suggerisce il nome, è specifico dell’ap-plication server JBoss e specifica il class loader della appli-cazione (cfr. Listato 4).Analizziamo i file di configurazione del modulo web, rubri-ca_web.war. All’interno della cartella WEB-INF sono pre-senti tre file XML di configurazione:

§ web.xml;§ faces-config.xml;§ components.xml.

Il primo, web.xml, è proprio di una applicazione web Java. In questo, relativamente a Seam, è definito un listener re-sponsabile del bootstrapping del framework e della distru-zione dei contesti di sessione e di applicazione:

<listener> <listener-class> org.jboss.seam.servlet.SeamListener </listener-class></listener>

Il file faces-config.xml è proprio del framework JSF (cfr. [JJ1]). In questo, relativamente a Seam, deve essere defi-nito un phase listener responsabile della interazione di Seam

FIGURA 5 Il packaging dell’applicazione

Page 18: j2007 04 jj5

JAVA Journal

n.5 - novembre/febbraio 200818

speciale JBOSS Seam JAVA Journal

n.5 - novembre/febbraio 2008 19

JBOSS Seam speciale

LISTATO 2 Il codice della pagina Web editUtente.jspx

<?xml version=”1.0”?>

<jsp:root xmlns:jsp=”http://java.sun.com/JSP/Page” xmlns:h=”http://java.sun.com/jsf/html” xmlns:f=”http://java.sun.com/jsf/core” xmlns:s=”http://jboss.com/products/seam/taglib” xmlns=”http://www.w3.org/1999/xhtml” version=”2.0”><jsp:output doctype-root-element=”html” doctype-public=”-//W3C//DTD XHTML 1.0 Transitional//EN” doctype-system=”http://www.w3c.org/TR/xhtml1/DTD/ xhtml1-transitional.dtd”/><jsp:directive.page contentType=”text/html”/><html><head> <title>Inserisci un nuovo utente</title> <link rel=”stylesheet” type=”text/css” href=”css/style.css” /></head><body> <f:view> <h:form> <f:facet name=”beforeInvalidField”> <h:outputText value=”X” styleClass=”erroreInput” /> </f:facet> <f:facet name=”afterInvalidField”> <s:message styleClass=”erroreInput” /> </f:facet> <h:messages globalOnly=”true” styleClass=”messaggio”/> <table border=”0”> <s:validateAll> <tr> <td>Nome*</td> <td><s:decorate> <h:inputText id=”txt_nome” value=”#{utente.nome}” required=”true”/> </s:decorate> </td> </tr> <tr> <td>Cognome*</td> <td><s:decorate> <h:inputText value=”#{utente.cognome}” required=”true”/> </s:decorate> </td> </tr> <tr> <td>Telefono*</td> <td><s:decorate> <h:inputText value=”#{utente.telefono}” required=”true”/> </s:decorate> </td> </tr> <tr> <td>Indirizzo</td> <td><s:decorate> <h:inputText value=”#{utente.indirizzo}”/> </s:decorate> </td> </tr> </s:validateAll> </table> <h:commandButton value=”Salva” action=”#{rubrica.salva}”/> </h:form> </f:view> </body> </html></jsp:root>

Page 19: j2007 04 jj5

JAVA Journal

n.5 - novembre/febbraio 200818

speciale JBOSS Seam JAVA Journal

n.5 - novembre/febbraio 2008 19

JBOSS Seam speciale

LISTATO 3 Il file application.xml

<?xml version=”1.0” encoding=”UTF-8”?><application xmlns=”http://java.sun.com/xml/ns/javaee” xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xsi:schemaLocation=”http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/application_5.xsd” version=”5”> <display-name>Rubrica Application</display-name> <module> <web> <web-uri>rubrica_web.war</web-uri> <context-root>/rubrica</context-root> </web> </module> <module> <ejb>rubrica_ejb.jar</ejb> </module> <module> <java>jboss-seam.jar</java> </module> <module> <java>el-api.jar</java> </module> <module> <java>el-ri.jar</java> </module></application>

con il ciclo di vita di una richiesta JSF:

<lifecycle> <phase-listener> org.jboss.seam.jsf.SeamPhaseListener </phase-listener></lifecycle>

Infine, il file components.xml (cfr. Listato 5) è specifi-co del framework Seam ed è usato per una configura-zione avanzata dei componenti, sia di quelli nativi sia di quelli definiti nell’applicazione. Nella configurazio-ne di base abbiamo solo specificato il pattern jndi con il quale il framework recupera, in modo trasparente per lo sviluppatore, le reference degli EJB Session durante le operazioni di lookup:

<core:init jndi-pattern= ”rubrica_EAR/#{ejbName}local”/>

Osserviamo che tale valore è specifico dell’application server, non esistendo uno standard nella specifica de-gli EJB versione 3.0 (cfr. [JJ2a]). Prima di conclude-re questa panoramica sul modulo web osserviamo che nella directory WEB-INF/lib è presente il file jboss-seam-ui.jar.

Analizziamo il modulo EJB, rubrica_ejb.jar. All’interno della cartella META-INF sono presenti due file XML di configurazione:

§ ejb-jar.xml;§ persistence.xml.

Il primo specifica un interceptor (cfr. [JJ2a]) che regola l’interazione di Seam con gli EJB dell’applicazione:

<ejb-jar> … <interceptor-binding> <ejb-name>*</ejb-name> <interceptor-class> org.jboss.seam.ejb.SeamInterceptor </interceptor-class> </interceptor-binding> …</ejb-jar>

Il secondo, il file persistence.xml (cfr. [JJ3]), è necessa-rio per la configurazione di JPA, definendo in partico-lare Hibernate come provider:

…<persistence>

LISTATO 4 Il file jboss-app.xml

<jboss-app> <loader-repository> seam.jboss.org:loader=rubrica_EAR </loader-repository> </jboss-app>

LISTATO 5 Il file components.xml

<?xml version=”1.0” encoding=”UTF-8”?><components xmlns=”http://jboss.com/products/seam/components” xmlns:core=”http://jboss.com/products/seam/core” xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance” xsi:schemaLocation=“http://jboss.com/products/seam/core http://jboss.com/products/seam/core-1.2.xsd http://jboss.com/products/seam/components http://jboss.com/products/seam/components-1.2.xsd”>

<core:init jndi-pattern=”rubrica_EAR/#{ejbName}/local”/> <core:ejb installed=”false”/></components>

Page 20: j2007 04 jj5

JAVA Journal

n.5 - novembre/febbraio 200820

speciale JBOSS Seam

Note Biografiche

Fabio Staro, dottore in Fisica è Responsabile Tecnico per i pro-getti Java presso la Direzione Ricerca ed Innovazione di Engi-neering Ingegneria Informatica S.p.A.

<persistence-unit name=”userDatabase”> <provider> org.hibernate.ejb.HibernatePersistence </provider> <jta-data-source> java:/theDataSource </jta-data-source> <properties> <property name=»hibernate.hbm2ddl.auto» value=»create-drop»/> </properties></persistence>…

Prima di concludere osserviamo che nell’archivio rubrica_ejb.jar è presente il file seam.properties. La cosa curiosa è che il file è vuoto ed è tuttavia fondamentale! Infatti allo startup Seam cerca i suoi componenti (le classi annotate con @Name) in tutti gli archivi che contengono un file, anche vuoto, il cui nome è appunto seam.properties.

Conclusioni

In questo articolo abbiamo iniziato ad apprezzare i benefici introdotti con JBoss Seam. Abbiamo anche accennato al concetto di conversa-zione utente (conversation context) e alla relativa gestione.

Riferimenti e Bibliografia

[1]: http://www.jboss.com/.[2]: http://www.mysql.com/[3]: http://www.hibernate.org/[4]: JEMS Installer: http://labs.jboss.com/jemsinstaller/[5]: Web Beans JSR 299 (http://www.jcp.org/en/jsr/detail?id=299). [6]: Open Session In View Pattern: http://www.jroller.com/cardsharp/entry/open_session_in_view_pattern [7]: Download Jboss Seam: http://www.jboss.com/products/seam[JJ1] : “Speciale JSF”. Java Journal N. 1[JJ2a] : “Introduzione agli EJB 3.0”. Java Journal N. 3.[JJ2b] : “Orizzonti Enterprise” Java Journal N. 3.[JJ3] : “JPA (prima parte)”. Java Journal N. 4.[JSF]: De Mann, Kito, “JavaServer Faces In Action”, Manning Publica-tions Co 2005.[MEJ]: Sriganesh R. P., Brose G., Silverman M. “Mastering Enterprise JavaBeans 3.0”, Wiley Publishing, Inc. [BAD]: Kodali Raghu R., Wetherbee J. e Zadrozny P., “Beginning EJB 3 Application Development”, Apress. [JPH]: Bauer C. e King G., “Java Persistence with Hibernate”, Manning Publications Co.

Page 21: j2007 04 jj5

“Algoritmi Genetici in .NET”di Fabio Fabozzi

Anno 2008

100 pagine, formato 17x24 cm

ISBN 9788881500314

Ordinalo su

www.shopinfomedia.com

“Algoritmi Genetici in .NET”

solo15,00 Euro

Perchè scrivere un libro dedicato agli AG? Anzitutto, per la necessità di sottolineare l’esigenza di un approccio diverso alle scienze delle computazioni mentali. Non si può ignorare il fatto che la nostra struttura biologica e l’interazione tra le persone e gli oggetti presenti nell’universo che ci circonda abbiano delle connotazioni che sfuggono dal calcolo sublime o da promettenti simulazioni meccanicistiche. La natura è ciò che è: difficilmente prevedibile. Ma se ci si chiedesse, o se ci si ponesse in grado, di avvicinarsi con una certa accuratezza alla natura e con una certa approssimazione, allora le applicazioni che si vogliono imporre come “intelligenti” potrebbero presentare un certo grado di accuratezza o essere “verosimili”, rispetto alla realtà che ci circonda. Nonostante possa apparire come una sorta di utopia scientifica, in realtà si tratta di una questione di osser-vazione tra ciò che è l’osservatore e ciò che è osservato. È la loro interdipendenza che crea una dinamicità irripetibile.Gli AG rappresentano un modo di fare scienza presuntuoso e anche corretto, per il numero di problemi che si possono risolvere attraverso essi; perchè? La risposta è semplice: si tratta di una scienza che tiene in pregio un concetto molto importante; si tratta cioè di conoscere “la storia” di un sistema che si sta studiando e la sua evoluzione nel tempo ed in che modo accadrà tale possibile evoluzione.Spero che l’utilizzo dei linguaggi VB.NET e C#, impiegati nella realizzazione degli esempi di questo libro, ne faciliti la diffusione.

Page 22: j2007 04 jj5
Page 23: j2007 04 jj5

JAVA Journal

23n.5 - novembre/febbraio 2008

educational

DAO Generator

Realizzare un’applicazione Web resistente alle migrazioni tra DBMS

>> di Riccardo Solimena ([email protected])

Non sempre, quando iniziamo a scri-vere un progetto, sappiamo se use-remo per sempre un determinato DBMS, oppure se nel corso del tem-po cambierà.A priori, di conseguenza, converreb-

be premunirsi da questi cambiamenti disegnando un’architettura robusta e con dei package di imple-mentazione facilmente intercambiabili.Infatti, a meno che non si stiano utilizzando prodotti come Hybernate (che si interpone come middlewa-re parlando nel dialetto SQL del DBMS che dobbia-mo utilizzare), abbiamo bisogno per ogni DBMS di un “matching” appropriato tra SQL e tipi di dati, dal DBMS a JAVA. Ovviamente, senza le giuste accor-tezze in fase di progettazione, il refactoring del Da-taLayer comporta problemi e costi di manutenzione elevatissimi; inoltre, non rimane traccia della vecchia versione della nostra implementazione se non in ob-soleti backup.

La soluzione

Lo scopo è realizzare dei data layer Java paralleli che attraverso semplici switch ci esonerino da una inutile e costosa attività di manutenzione.Vedremo come attraverso semplici strumenti (uno fi-sico e due concettuali) si possa riuscire in questo in-tento.Parleremo fondamentalmente di pattern e di best practice, passando così, come da titolo, dal DAO Ge-nerator per creare la base del data layer, al Connec-tion Factory per la gestione ottimale delle connes-sioni, all’Abstract Factory per rendere trasparente il richiamo delle implementazioni delle varie logiche parallele di data layer, al cache manager per gestire al meglio le risorse in gioco. Vediamo subito DAO Ge-nerator, un potente strumento per i lavori cosiddetti “di massa”.

DAO Generator

Quando si progettano applicazioni suddivise in di-versi tier (Presentation/Business/Data Access/Value Object) una tra le operazioni più gravose consiste nella configurazione e nella creazione dei Data Ac-cess Object (DAO), che spesso sono il vero “core” del-l’applicazione. Ma per scrivere bene i DAO lo sforzo è notevole, soprattutto quando sia le tabelle sia i cam-pi sono in numero elevato. Infatti, i passi da esegui-re sono:

§ costruire oggetti Java che rispecchiano la struttura delle tabelle e degli attributi del DB;

§ scrivere per ogni tabella e per ogni chiave primaria del DB un Value Object (VO);

§ scrivere un’interfaccia con i metodi (e le ec-cezioni) relativi ad ogni singola tabella;

Nelle applicazioni

multitier una delle

operazioni più gravose

è la configurazione e la

creazione dei DAO

Page 24: j2007 04 jj5

JAVA Journal

n.5 - novembre/febbraio 200824

educational JAVA Journal

n.5 - novembre/febbraio 2008 25

educational

§ implementare per ogni interfaccia una classe DAO relativa.

Il lavoro è gravoso, ma i benefici sono notevoli, in quanto, implementando il livello Data Acces, gli altri livelli riman-gono immutati se un giorno la struttura del DB dovesse mai cambiare (spero non vi accada mai, ma purtroppo … accade spesso).Come dicevo, il lavoro è abbastanza impegnativo, poiché di codice da scrivere ve ne è tanto e può accadere di dimenti-carsi di gestire alcune eccezioni, oppure di utilizzare come parametro di input una connessione cercando di istanziar-la all’interno del DAO stesso, con ovvie ripercussioni sul nostro progetto.Per fortuna c’è chi, come akcess, ha realizza-to un software del tutto open-source che non solo rispetta i maggiori standard di stesura dei DAO, ma effettua un reverse engineering da molti DBMS esistenti in commercio, tra cui MySql, Oracle, SQL Server, HSQLDB, Po-stgres, ecc.Stiamo parlando di DAO Generator, questo mago della lampada di casa akcess, che data la struttura di un DB fa al posto nostro tutto il lavoro descritto prima, comprese tutte le query di base, la gestione delle eccezioni e delle con-nessioni del caso, per non parlare dell’overri-ding del metodo toString per ogni VO.La semplicità del prodotto lascia sbalorditi, so-prattutto per la libertà di personalizzazione dei nostri package.Il prodotto non va installato, e l’eventuale con-

figurazione in caso di incompatibilità tra le impostazioni della JAVA_HOME è alquanto banale: infatti, basterà mo-dificare con un qualsiasi editor di testi il file BAT (al mo-mento di stesura di questo articolo il software è giunto alla versione 1.2).

Lanciato il programma dal file run.bat, il programma mo-stra la schermata riportata in Figura 1. La semplice in-terfaccia ha un menu essenziale e allo stesso tempo com-pleto.

Generiamo il layer DAO

Dal menù File sarà sufficiente inserire il nome del nuo-vo progetto e la cartella in cui andrà a risiedere il file di

FIGURA 1 Schermata principale di DAO Generator

FIGURA 2 Creazione di un nuovo progetto

Page 25: j2007 04 jj5

JAVA Journal

n.5 - novembre/febbraio 200824

educational JAVA Journal

n.5 - novembre/febbraio 2008 25

educational

salvataggio (Figura 2). Una volta creato il pro-getto sarà necessario dire al programma qual è il DBMS con cui dovrà dialogare e la posizione della libreria collettore (Figura 3). Il passo suc-cessivo è effettuare il login nel database; biso-gnerà quindi specificare la URL, lo user id e la password associate al DB ed effettuare la con-nessione (Figura 4).Se abbiamo fornito correttamente tutte le in-formazioni richieste, apparirà nella console po-sizionata in basso nella finestra di DAO Gene-rator il messaggio di avvenuta connessione (Fi-gura 5).Dal menu a tendina (o dalle icone della sotto-stante toolbar) sarà possibile selezionare l’op-zione Generate DAO: così facendo si aprirà la fine-stra di selezione delle tabelle (Figura 6). A que-sto punto, si selezionano le tabelle di cui si vuole effettuare il “reverse engineering” e si passa al passo successivo di mappatura tra tabelle e og-getti (Figura 7).Fatto ciò, sarà necessario specificare la cartella in cui si vuole salvare il package, il percorso del package e i nomi dei singoli sottopackage (Fi-gura 8), tra cui DAO, VO e addirittura Exception, Implementation e Factory! Fantastico no?

A partire da appena quattro tabelle, conte-nenti in totale solamente 19 attributi, il bravo DAO Generator ha scritto per noi 39 file Java (che però dovremo compilare da noi … uno sforzo accettabile no?) e sette cartelle. Il tut-to servito su un piatto d’argento: basta vedere la quantità (e la qualità) di codice scritto nei file (soprattutto il codice SQL) per capire il la-voro risparmiato interagendo con cinque sem-plici finestre. Il sito web del progetto DAO Ge-nerator è disponibile all’indirizzo https://dao-generator.dev.java.net/

Ovviamente fornendo uno strato di Imple-mentation, il codice generato è già sufficien-te per avere un’applicazione che rispetta il pattern DAO (Figura 9). Come si nota, gli

FIGURA 4 Specifica delle credenziali di accesso al database

FIGURA 3 Selezione del DBMS

FIGURA 5 Messaggio di avvenuta connessione

FIGURA 6 Selezione delle tabelle

A partire da 4 tabelle

con 19 attributi

DAO Generator ha

prodotto 39 file Java

Page 26: j2007 04 jj5

JAVA Journal

n.5 - novembre/febbraio 200826

educational JAVA Journal

n.5 - novembre/febbraio 2008 27

educational

strati costruiti sono:

§ il layer Abstract Factory, ossia il “core” dell’appli-cazione, in quanto punto di accesso a tutte le al-tre classi.

§ il layer DAO Factory, ovvero le interfacce per le classi DAO.

§ il layer Implementation, che comprende l’imple-mentazione vera e propria dei metodi utilizzan-do le Factory.

§ il layer Exception, fondamentale per la gestione personalizzata degli errori.

§ il layer Entity, ossia i value object che rispecchia-no la struttura del database in chiave Java.

§ il layer Primary Key

Nel layer Primary Key c’è un po’ di difficoltà indotta nella gestione degli entity, ma avete idea di che comodità sia ge-stire le PK come oggetti? … No? L’avrete ben presto!Inoltre ad ogni classe è associato uno strato intero di COUNT, utilissimo per le comunissime operazioni di con-teggio dei dati presenti nelle singole tabelle.

Connection Factory

Prima di passare a sfruttare il codice appena ge-nerato dal tool DAO Generator, teniamo conto che tutto il livello data layer necessita di una con-nessione attiva di tipo Connection, perciò imple-mentiamo un mini-tool per la gestione dinami-ca della connessione verso i DBMS.Poiché, e non mi stancherò mai di dirlo, le con-nessioni vanno passate ai DAO e non vanno ge-nerate all’interno di ognuno di essi (per ovvi mo-tivi di scalabilità, sicurezza e manutenibilità), abbiamo bisogno di una classe che dati alcuni parametri (tra cui ad esempio il nome del DBMS a cui connettersi), restituisca un’istanza di Con-nection da passare a tutti i DAO e non solo.Partendo dal presupposto di fornire un tool di connessione, dobbiamo prevedere N classi di connessione per gli N DBMS che vogliamo con-siderare, ognuno dei quali dovrà avere un meto-do che restituisca una connection di tipo

java.sql.Connection

A questo punto, abbiamo bisogno di una classe Manager, che amministri la connessione secon-do necessità. Una siffatta classe potrebbe avere un metodo del tipo riportato nel Listato 1; ma questa soluzione, se pur comoda e rapida, non rispecchia una robusta architettura e la manu-tenibilità potrebbe essere compromessa col pas-sare del tempo.Di conseguenza, sarà meglio affidarsi ad un’in-terfaccia per ogni tipo di connessione, assicu-rando oltretutto una corretta gestione delle pos-sibili eccezioni generate in fase di connessio-ne; e ciò per tutte le classi di connessione ai vari DBMS.

L’interfaccia può essere anche molto semplice come la se-guente:

public interface ConnectionInterface { public Connection connect() throws ConnectException, ClassNotFoundException, SQLException;}

Ovvero assicuriamo che il metodo implementato dalle classi di connessione abbia sempre lo stesso nome, garan-tendo la cattura delle stesse eccezioni per tutte le classi.A questo punto, appoggiandoci ad una classe di costanti, basterà realizzare delle classi di connessione di questo tipo, come si vede nel Listato 2 per la connessione a MySQL o nel Listato 3 per la connessione a Oracle. Potremmo anche utilizzare un sistema di switch (come gli If Else visti precedentemente) per smistare le varie connes-sioni; ma c’è un’altra scelta: utilizzare la Reflection per

FIGURA 7 Mapping tra tabelle e oggetti Java

FIGURA 8 Schermata di generazione dei DAO

Page 27: j2007 04 jj5

JAVA Journal

n.5 - novembre/febbraio 200826

educational JAVA Journal

n.5 - novembre/febbraio 2008 27

educational

FIGURA 9 Diagramma delle classi generate da DAO Generator

Page 28: j2007 04 jj5

JAVA Journal

n.5 - novembre/febbraio 200828

educational JAVA Journal

n.5 - novembre/febbraio 2008 29

educational

Ponendo che ProjectConstant.DBMS_PRE-FIX sia

org.solimena.javacourse.connection

che ProjectConstant.DBMS_SUFFIX sia “ Con-nection” e che DBMSName sia “MySQL”, l’in-tera stringa genererà:

org.solimena.javacourse.connection.MySQLConnection

e di conseguenza la Reflection non farà altro che trasformare ConnectionInterface factory = (ConnectionInterface)c.cast(c.newInstance());

in

ConnectionInterface factory = new MySQLConnection();

oppure in

ConnectionInterface factory = new OracleConnection();

e così via per gli altri tipi di DBMS.Se ci sono stati dei problemi il metodo prov-vederà a inviare l’eccezione, altrimenti pas-serà semplicemente la connessione al meto-do chiamante.

Partendo da questo presupposto, per poter asserire di aver fornito un buon servizio di connessione trasparente anche a livello di Business, servirebbe un metodo che in auto-matico restituisca la connessione di default

scelta dall’amministratore o dallo sviluppatore del siste-ma, senza dover ogni volta conoscere e inserire le informa-zioni necessarie. Un modo semplice ed elegante è ricorrere ancora una volta a un file di costanti e prelevare la connes-sione di default scelta dall’utente e sfruttarla all’interno della logica di un metodo:

public Connection getDefaultConnection() throws ConnectException, ClassNotFoundException, SQLException, InstantiationException, IllegalAccessException{ return getConnection( ProjectConstant.DBMS_CONNECTION);}

istanziare l’apposita classe di connessione. È una scelta più dispendiosa, ma che non necessita di manutenzione. Infatti, in questo modo il codice può diventare:

public Connection getConnection(String DBMSName) throws ConnectException,ClassNotFoundException, SQLException,InstantiationException, IllegalAccessException{ Class c = Class.forName( ProjectConstant.DBMS_PREFIX + DBMSName + ProjectConstant.DBMS_SUFFIX); ConnectionInterface factory = (ConnectionInterface)c.cast(c.newInstance()); Connection connection = factory.connect(); return connection;}

LISTATO 1 Il metodo GetConnection

public Connection getConnection(String DBMSName) throws ConnectException, ClassNotFoundException, SQLException{ Connection connection = null; if(“MySQL”.equals(DBMSName)){ connection = new MySQLConnection().connect(); } else if(“Oracle”.equals(DBMSName)){ connection = new OracleConnection().connect(); } return connection; }

LISTATO 2 La classe MySQLConnection

public class MySQLConnection implements ConnectionInterface{

public Connection connect() throws ConnectException, ClassNotFoundException, SQLException { Connection conn = null;

String username = ProjectConstant.DBMS_MYSQL_USERNAME; String password = ProjectConstant.DBMS_MYSQL_PASSWORD;

Class.forName( ProjectConstant.DBMS_MYSQL_DRIVER);

String url = ProjectConstant.DBMS_MYSQL_URL;

conn = DriverManager.getConnection( url,username, password); return conn; } }

Page 29: j2007 04 jj5

JAVA Journal

n.5 - novembre/febbraio 200828

educational JAVA Journal

n.5 - novembre/febbraio 2008 29

educational

dove ProjectConstant.DBMS_CONNECTION è il tipo del DBMS che vogliamo usare per default (MySQL, Oracle, ecc.).

A questo punto per ottenere una connessio-ne, indipendentemente dal tipo di DBMS adottato, sarà sufficiente una sola riga di codice, peraltro autoesplicativa:

Connection conn = new ConnectionFactory(). getDefaultConnection();

Pur essendo un po’ articolata internamente, questa architettura offre una forte versatili-tà, affidabilità e leggibilità per l’utilizzatore; infatti, utilizzando una semplice classe main è possibile intuire gli innumerevoli benefici di questo approccio (Listato 4).Come si è già detto, se la connessione è an-data a buon fine siamo stati in grado di istanziarla con una sola riga di codice! Inol-tre, disponiamo di un’efficiente sistema di gestione degli errori per poter individuare immediatamente il problema e risolverlo senza perderci in eccezioni generiche.Un ultimo appunto, ma di importanza forse superiore, è ciò che ho racchiuso nella clau-sola finally, in quanto nel 99% dei casi (an-che in progetti di grosse dimensioni) non è difficile che le connessioni vengano aperte e mai chiuse. Con l’utilizzo di finally in questo modo, ci assicuriamo che in qualsiasi caso, eccezione o no, la connessione verrà comun-que chiusa, altrimenti la connessione stessa rimane “appesa” incrementando l’utilizzo di memoria nel server e una temporanea pa-ralisi del DBMS e quindi dell’applicazione! Bisogna chiudere sempre e comunque le con-nessioni una volta aperte, e il metodo final-ly, spesso trascurato, dà l’opportunità di non sbagliare, perciò sfruttiamola.Le costanti utilizzate nella classe ProjectCon-stant sono riportate nel Listato 5.Ovviamente è consigliato largamente l’uti-lizzo di file statici di proprietà in modo tale da non dover mettere mano al codice nel caso di modifica delle impostazioni di base quali nome del DB e credenziali di accesso.In Figura 10 è riportato il diagramma delle classi realizzate.

Abstract Factory

Una volta fornito un package affidabile di connessioni torniamo indietro a ciò che DAO Generator ha creato per noi.Osserviamo la classe DAOFactoryCreater. Pur-troppo, il metodo DAOFactoryCreater creato da DAOGen è ottimo per creare una nuova serie di DAO, ma saranno DAO tutti dello

LISTATO 3 La classe OracleConnection

public class OracleConnection implements ConnectionInterface{

public Connection connect() throws ConnectException, ClassNotFoundException, SQLException { Connection conn = null;

String username = ProjectConstant.DBMS_ORACLE_USERNAME; String password = ProjectConstant.DBMS_ORACLE_PASSWORD;

Class.forName( ProjectConstant.DBMS_ORACLE_DRIVER);

String url = ProjectConstant.DBMS_ORACLE_URL;

conn = DriverManager.getConnection( url,username, password); return conn; }

}

LISTATO 4 La classe Lunch

public class Launch{ public static void main(String[] args) { Connection conn = null; try { conn = new ConnectionFactory().getDefaultConnection(); System.out.println(“Connection estabilished”);

//inserire qui il proprio codice

} catch (ConnectException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } finally { if(conn != null){ try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }

}}

Page 30: j2007 04 jj5

JAVA Journal

n.5 - novembre/febbraio 200830

educational JAVA Journal

n.5 - novembre/febbraio 2008 31

educational

stesso DB. Di conseguenza, per realizzare un’applicazione che effet-tui un rapido switch da un DBMS ad un altro, è necessario modificarlo opportunamente per il supporto multi DB. Il codice della classe creata da DAO Generator è il seguen-te:

package org.solimena.javacourse.factory;public class DAOFactoryCreater{

public DAOFactory getFactory(){ return new DAOFactoryImpl(); }}

Per ottenere i risultati desiderati bisogna mo-dificare questo generatore di Factory come è riportato nel Listato 6. Come si vede, sfrut-tando lo stesso pattern adottato per la con-nessione è molto semplice gestire le imple-mentazioni dei singoli Factory. Ma è stato in-dispensabile modificare la tipologia di classe in Abstract.DAO Generator ha creato dei DAOFactoryImpl (Figura 11), ossia delle implementazioni del-l’interfaccia DAOFactory, ma, come si intuisce, è necessaria un’implementazione per ogni DBMS (basterà quindi ricreare, selezionando il dialetto opportuno in DAO Generator, un nuovo insieme di classi ). Adesso che abbia-mo creato questo strato di Abstract Factory (in poche parole una Factory di Factory) le implementazioni di DAOFactory che prima erano nella forma:

public class DAOFactoryImpl implements Serializable, DAOFactory

saranno ora cambiate in:

public class DAOFactoryImplMySQL extends DAOFactoryCreater

FIGURA 11 Overview Abstract Factory DAO

FIGURA 10 Overview Connection Factory

Page 31: j2007 04 jj5

JAVA Journal

n.5 - novembre/febbraio 200830

educational JAVA Journal

n.5 - novembre/febbraio 2008 31

educational

e così via per tutti gli altri DBMS di cui abbiamo necessi-tà.

Dal momento che l’interfaccia di DAOFactory è stata adot-tata in DAOFactoryCreater (che a sua volta è stato modifi-cato aggiungendo altri metodi) è ovvia e indispensabile la modifica di implements della vecchia classe in extends nella classe modificata.Così come accennato prima, per ogni implementazione dobbiamo ricreare un export con DAO Generator e le classi

FIGURA 12 Package Explorer

da ricopiare nel nostro progetto saranno quelle sotto-stanti il livello DAOFactoryCreater così come mostra la struttura del progetto nella Figura 12.

CacheManager

La tecnica di Reflection utilizzata per il package di connessione, se pur comoda, può rivelarsi molto di-spendiosa. Infatti, il costo in termini di tempo per istanziare una classe con questa tecnica è circa il doppio rispetto all’istanziamento diretto. Inoltre, per quanto riguarda la gestione dei DBMS, anche la sud-divisione in un numero elevato di livelli, può appe-santire il sistema. Sarebbe quindi opportuno adottare un sistema cache manager per memorizzare le infor-mazioni una volta che le classi hanno caricato tut-ti i dati.Vi sono diverse tecniche per gestire la cache, e spesso molti programmatori non si accorgono che bastereb-be veramente poco per gestire al meglio le operazio-ni più comuni, e soprattutto gli oggetti in memoria, senza perdersi nell’utilizzo di oggetti di sessione (che successivamente sarà difficile gestire).Per l’esempio in questione è stato sufficiente realizza-re una semplicissima classe che funge da cache ma-nager per risolvere i problemi appena evidenziati di prestazione e gestione degli oggetti.Poiché gestisce oggetti contenuti nella HttpServletRe-quest, il nostro CacheManager estenderà, a seconda del-la tecnologia che decidiamo di adottare (Servlet, JSF, Struts, ecc.), l’apposita classe (HttpServlet, Action, Di-spatchAction) in modo da gestire l’apposito contenitore di Session (Listato 7).

Il concetto è di inserire i dati di interesse, una volta caricati, all’interno di un HashMap e a sua volta me-morizzare l’intera HashMap all’interno di un oggetto Session in modo tale che le informazioni siano disponi-bili ad ogni classe che le richieda e cha abbia la possi-bilità di passare la Request.La semplice funzione getSetCache prende in input solo la richiesta ed effettua un controllo sulla sessione. Se la cache HashMap non è presente in Session, verranno dapprima effettuati i dispendiosi calcoli e successiva-mente inserite le informazioni all’interno della cache HashMap, la quale verrà subito inserita nella Session. Se invece la cache è già presente in Session, il Cache-Manager semplicemente la restituirà.Il recupero degli oggetti è abbastanza semplice, infatti all’interno delle nostre classi di Presentation (Servlet, ManagedBean di JSF, Action di Struts) una volta este-sa alla classe CacheManager, saranno sufficienti le tre seguenti righe di codice per recuperare i due oggetti caricati dinamicamente in sessione:

HashMap cache = getSetCache(request);Connection connection = (Connection)cache.get(“CONNECTION”);

FIGURA 13 Tabella Elenco Libri

FIGURA 14 Tabella Dettaglio

Page 32: j2007 04 jj5

JAVA Journal

n.5 - novembre/febbraio 200832

educational JAVA Journal

n.5 - novembre/febbraio 2008 33

educational

DAOFactory daoFactory = (DAOFactory)cache.get(“DAO_FACTORY”);

Interazione tra i componenti

A questo punto possiamo radu-nare tutti i concetti esposti fino-ra e vedere come sarà semplice realizzare delle classi di Presen-tation ben strutturate.Nella semplice classe di Action Struts riportata nel Listato 8 ci sono tutti i concetti evidenziati finora.A partire dal recupero dei dati nel CacheManager, vediamo come il daoFactory ci permetta, con dei semplici comandi, di ot-tenere delle funzionalità di alto livello; infatti, daoFactory.createBookDAO() crea il BookDAO relati-vo al DBMS selezionato prece-dentemente dall’utente (attra-verso l’utilizzo del file di costan-ti) in modo del tutto trasparente per l’utente. Come si può notare, non appare nessun riferimento al DBMS. Il metodo invocato di BookDAO, findAll(connection), necessita so-lamente della connessione, assi-curando, così come detto prece-dentemente, una corretta gestio-ne della connessione, in quanto l’istanza, la gestione delle ecce-zioni e la chiusura della connes-sione vanno gestiti ad alto livello e non nei singoli metodi.Con una sola riga di codice ab-biamo ottenuto l’elenco dei Libri presenti in archivio.DAOGen gestisce gli oggetti multipli come Array di oggetti; dato che per il lato presentation gli array sono a volte ingestibi-li con alcuni framework, è stato adottato un metodo di utilità che converta gli Array di og-getti in ArrayList di oggetti

JavaUtils.getArraylistFromArray(books);

in modo da passare infine il risultato nella request con il comando:

request.setAttribute(“results”, results);

LISTATO 5 Costanti utilizzate nella classe ProjectConstant

public static final String DBMS_CONNECTION = getPropValue(“dbms.connection”);

//MYSQLpublic static final String DBMS_CONNECTION = “MySQL”;public static final String DBMS_MYSQL_USERNAME = “root”;public static final String DBMS_MYSQL_PASSWORD = “password”;public static final String DBMS_MYSQL_DRIVER = “com.mysql.jdbc.Driver”;public static final String DBMS_MYSQL_URL = “jdbc:mysql://localhost:3306/javacourse”;

//ORACLE// analogo ... public static final String DBMS_ORACLE_USERNAME = “…”;public static final String DBMS_ORACLE_PASSWORD = “…”;public static final String DBMS_ORACLE_DRIVER = “…”;public static final String DBMS_ORACLE_URL = “…”;public static final String DBMS_PREFIX = “…”;public static final String DBMS_SUFFIX = “…”;

LISTATO 6 La classe DAOFactory

public abstract class DAOFactoryCreater implements DAOFactory { private static DAOFactoryCreaterNew getDAOFactory(String dbType) { if(dbType.equals(ProjectConstant.DBMS_CONNECTION_MYSQL)){ return new DAOFactoryImplMySQL(); } else if(dbType.equals( ProjectConstant.DBMS_CONNECTION_ORACLE)){ return new DAOFactoryImplOracle(); } else{ return new DAOFactoryImplMySQL(); } } public static DAOFactoryCreater getFactory(){ return getDAOFactory(ProjectConstant.DBMS_CONNECTION); }}

Avendo implementato le factory in modo da rimandare le eccezioni al livello superiore, la gestione delle eccezio-ni viene effettuata solamente in tutte le classi di presen-tation; assicurando così una gestione centralizzata note-volmente semplificata. Infatti, in questo modo è possibile personalizzare il messaggio di errore in base al tipo di ec-cezione scatenata e rimandare l’errore stesso al client dal-l’opportuna pagina di errore.Quando si progettano applicativi basati sull’utilizzo di dati provenienti da database, uno tra i concetti più importanti è la chiusura della connessione dopo il suo utilizzo, come abbiamo già detto.

A questo punto non resta che evidenziare i dati caricati

Page 33: j2007 04 jj5

JAVA Journal

n.5 - novembre/febbraio 200832

educational JAVA Journal

n.5 - novembre/febbraio 2008 33

educational

LISTATO 7 La classe CacheManager

public class CacheManager extends DispatchAction { private HashMap cache = new HashMap(); private HttpSession session; private DAOFactory daoFactory; private Connection connection; private String result = “”;

public HashMap updateCache(String name, Object value){

if(!cache.containsValue(name)){

cache.put(name, value); session.setAttribute(“SESSION”, cache);

}

return cache; }

public HashMap getSetCache(HttpServletRequest request) throws ConnectException, ClassNotFoundException, SQLException,InstantiationException, IllegalAccessException {

session = request.getSession();

if(session.getAttribute(“SESSION”) == null || session.getAttribute(“SESSION”).equals(“”)) { //insert Connection connection = new Connection Factory().getDefaultConnection(); cache.put(“CONNECTION”, connection); //insert DAOFactory daoFactory = DAOFactoryCreaterNew.getFactory(); cache.put(“DAO_FACTORY”, daoFactory); session.setAttribute(“SESSION”, cache); }

return cache; }}

Page 34: j2007 04 jj5

JAVA Journal

n.5 - novembre/febbraio 200834

educational JAVA Journal

n.5 - novembre/febbraio 2008 35

educational

LISTATO 8 La classe BooksAction

public class BooksAction extends CacheManager { private Connection connection; private DAOFactory daoFactory; private String result;

public ActionForward findAll(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) {

try {

HashMap cache = getSetCache(request); connection = (Connection)cache.get(“CONNECTION”); daoFactory = (DAOFactory)cache.get(“DAO_FACTORY”); Book[] books = daoFactory.createBookDAO().findAll(connection); ArrayList results = JavaUtils.getArraylistFromArray(books); request.setAttribute(“results”, results);

} catch (ConnectException e) { request.setAttribute(“error”, e); e.printStackTrace(); } catch (ClassNotFoundException e) { request.setAttribute(“error”, e); e.printStackTrace(); } catch (SQLException e) { request.setAttribute(“error”, e); e.printStackTrace(); } catch (InstantiationException e) { request.setAttribute(“error”, e); e.printStackTrace(); } catch (IllegalAccessException e) { request.setAttribute(“error”, e); e.printStackTrace(); }

finally {

try {

if(connection != null) connection.close(); result = “success”;

} catch (final SQLException e) {

result = “failure”; request.setAttribute(“error”, e); e.printStackTrace();

}

finally{ return mapping.findForward(result); } } }}

Page 35: j2007 04 jj5

JAVA Journal

n.5 - novembre/febbraio 200834

educational JAVA Journal

n.5 - novembre/febbraio 2008 35

educational

LISTATO 9 La pagina di visualizzazione dell’elenco degli Item

<%@ taglib uri=”/tags/struts-bean” prefix=”bean”%><%@ taglib uri=”/tags/struts-logic” prefix=”logic”%>

<html><body> <logic:present name=”results”> <logic:notEmpty name=”results”> <table border=”1”> <tr> <td align=”center”>ID</td> <td align=”center”>NAME</td> <td align=”center”>PRICE (�)</td> </tr> <logic:iterate id=”book” name=”matrix”> <tr> <td> <a href=”books.do?action=findById&id=<bean:write name=”book” property=”id” />”> <bean:write name=”book” property=”id” /></a> </td> <td><bean:write name=”book” property=”name” /></td> <td><bean:write name=”book” property=”price” /></td> </tr> </logic:iterate> </table> </logic:notEmpty> <logic:empty name=”results”> La ricerca non ha prodotto risultati </logic:empty> </logic:present> <logic:notPresent name=”results”> Nessun oggetto presente. </logic:notPresent></body></html>

LISTATO 10 La pagina di visualizzazione dell’elenco degli articoli

…try { if(request.getParameter(“id”) != null && !request.getParameter(“id”).equals(“”)) { int id = Integer.parseInt(request.getParameter(“id”)); HashMap cache = getSetCache(request); connection = (Connection)cache.get(“CONNECTION”); daoFactory = (DAOFactory)cache.get(“DAO_FACTORY”); Book[] books = daoFactory.createBookDAO().findById(id, connection); ArrayList results = JavaUtils.getArraylistFromArray(books); request.setAttribute(“results”, results); } else{ result = “failure”; request.setAttribute(“error”, “No ID set!”); }} catch ……

Page 36: j2007 04 jj5

JAVA Journal

n.5 - novembre/febbraio 200836

educational JAVA Journal

n.5 - novembre/febbraio 2008 37

educational

LISTATO 11 Markup della pagina di dettaglio articolo

<%@ taglib uri=”/tags/struts-bean” prefix=”bean”%><%@ taglib uri=”/tags/struts-logic” prefix=”logic”%><html><body> <logic:present name=”results”> <logic:notEmpty name=”results”> <table border=”1”> <tr> <td align=”center”>ID</td> <td align=”center”>NAME</td> <td align=”center”>DESCRIPTION</td> <td align=”center”>PRICE</td> <td align=”center”>QUANTITY</td> </tr> <tr> <td><bean:write name=”book” property=”id” /></td> <td><bean:write name=”book” property=”name” /></td> <td><bean:write name=”book” property=”description” /></td> <td><bean:write name=”book” property=”price” /></td> <td><bean:write name=”book” property=”quantity” /></td> </tr> </table> </logic:notEmpty> <logic:empty name=”results”> La ricerca non ha prodotto risultati </logic:empty> </logic:present> <logic:notPresent name=”results”> Nessun oggetto presente. </logic:notPresent></body></html>

dalle nostre action. Per la Action in questione, adottando le Tag Libraries di Struts, la pagina di visualizzazione per l’elenco degli Items (Listato 9) produrrà un aspetto simile a quanto è mostrato nella Figura 13.

E anche il corpo del metodo Action “findById” sarà analo-go (Listato 10), così come la pagina di dettaglio (Listato 11 e Figura 14).

Conclusioni

Nonostante la complessità di strutturazione delle factory necessarie per ottenere dei metodi di alto livello, l’archi-tettura così creata fornisce in fase di sviluppo innumere-voli benefici: buona visibilità del codice, manutenibilità, estendibilità e una completa gestione delle eccezioni; ga-rantendo così una distinta robustezza dell’applicazione.Attraverso questi concetti, se sfruttati bene, non è diffi-cile ottenere dei package riusabili da adottare nei nostri progetti: dal package delle connessioni, al DAOFactory, a quello di CacheManager o alla JavaUtil che trasforma gli Array in ArrayList.In questo modo al programmatore finale sarà fornito un framework a tutti gli effetti, attraverso il quale, con po-che righe, potrà gestire rapidamente e con estrema puli-zia l’intero progetto, evitando di incappare negli errori più comuni.

DAOGen, nonostante generi Array di oggetti e non Collec-tions (operando così in vecchio stile), si può considerare a tutti gli effetti uno strumento indispensabile di generazio-ne DAO (per chi ragiona secondo questo pattern) anche perché ci può aiutare facilmente, durante le fasi di proget-to, nelle modifiche strutturali al DB, senza impattare sulle funzionalità di alto livello, grazie all’utilizzo delle opportu-ne Factory di creazione dei DAO.Gli argomenti trattati in questo articolo sono tanti e di si-curo hanno bisogno di essere assimilati con la pratica: il punto fondamentale è notare come Java, al solito, fornisca gli strumenti per realizzare sistemi complessi in termini di funzionalità, ma di semplice utilizzo per lo sviluppatore fi-nale. Il tutto facendo colloquiare più sistemi di piccole di-mensioni ma con funzionalità e scopi ben definiti.Le nozioni fornite, se pur basilari nella loro implementa-zione, sono una buona base per realizzare applicazioni web su cui potranno intervenire più sviluppatori, ma avendo ri-spettato gli standard. Buon lavoro a tutti.

Riferimenti

DAO Generator: https://dao-generator.dev.java.net/

Note Biografiche

Riccardo Solimena si occupa di sviluppo Java. Gestisce il sito www.riccardosolimena.com.

Page 37: j2007 04 jj5
Page 38: j2007 04 jj5

JAVA Journal

38 n.5 - novembre/febbraio 2008

focus JAVA Journal

n.5 - novembre/febbraio 2008 39

focus

JPA: Java Persistence APIseconda parte

Nel precedente articolo su JPA (acro-nimo di Java Persistence Api) [1] ab-biamo introdotto le caratteristiche base della specifica ed abbiamo ap-profondito la gestione delle relazioni tra le entità. Nell’articolo risaltava in

modo prepotente la radicale semplificazione presente con JPA rispetto alla precedente specifica per la per-sistenza dei dati in Java: gli Entity Bean versione 2.x. In questo articolo, conclusivo sull’argomento, appro-fondiamo ulteriormente la specifica soffermandoci sulle strategie di mapping per la gestione della ere-ditarietà, un design pattern frequente nel paradigma di sviluppo object-oriented e non presente nel mondo relazionale dei database. Gli esempi riportati realiz-zano un processo di sviluppo top-down: ossia, par-tendo dal modello delle classi deriveremo il disegno delle tabelle. Gli esempi sono stati verificati con l’application ser-ver JBoss versione 5.0 [2], con il database MySQL versione 5.0.27 [3] e con il provider JPA Hibernate [4].

Strategie per l’ereditarietà

Gli esempi che desideriamo realizzare sono voluta-mente semplificati, al fine di approfondire l’API e l’architettura JPA eliminando le complessità applica-tive legate ad un particolare dominio di interesse. Os-servando il diagramma delle classi riportato in Figu-ra 1 possiamo notare la semplice gerarchia presente tra le classi: Prodotto, Libro, Video, DVD e VHS. In par-ticolare, la classe astratta Prodotto è la root class della gerarchia e le classi Libro e Video la estendono diretta-mente. La classe Video è a sua volta astratta ed è este-sa dalle classi concrete DVD e VHS.

Tra le classi Prodotto e Autore è presente una relazione molti a molti bidirezionale. E tra le classi Autore ed In-dirizzo è presente una relazione uno a uno unidirezio-nale. La specifica JPA, per supportare in un database relazionale il concetto di ereditarietà, offre varie stra-tegie di mapping. Queste sono specificate nella enu-merazione javax.persistence.InheritanceType:

• SINGLE_TABLE;• JOINED;• TABLE_PER_CLASS;

In questo articolo analizzeremo le prime due strate-gie per il mapping delle entità tralasciando la strate-gia TABLE_PER_CLASS in quanto il suo supporto da parte dei provider JPA non è obbligatorio nella attua-le versione della specifica.

La strategia SINGLE_TABLE

Iniziamo con l’approfondire la strategia di mapping SINGLE_TABLE. Questa è la strategia di default, dove una sola tabella è usata per rappresentare l’intera ge-rarchia delle classi. Una colonna della tabella, assu-mendo valori differenti in base al tipo Java rappre-sentato dal record, è usata come discriminante per distinguere tra le sottoclassi della gerarchia.Di seguito riportiamo uno stralcio del codice della classe Prodotto:

@Entity(name=”Prodotto_Single”)@Table(name=”PRODOTTO_SINGLE”)@Inheritance(strategy=InheritanceType.SINGLE_TABLE)

La specifica JPA fornisce uno standard per la persistenza dei dati in Java, semplificando radicalmente la precedente tecnologia degli EJB di tipo Entity Bean. Tuttavia JPA non è una reingegnerizzazione della precedente tecnologia ma è di fatto una nuova architettura

>> di Fabio Staro ([email protected])

Page 39: j2007 04 jj5

JAVA Journal

38 n.5 - novembre/febbraio 2008

focus JAVA Journal

n.5 - novembre/febbraio 2008 39

focus

FIGURA 1 Il diagramma delle classi

@DiscriminatorColumn(name=”DISC”, discriminatorType=DiscriminatorType.STRING)public abstract class Prodotto {... @Id @GeneratedValue(strategy=GenerationType.AUTO) private int id; private String titolo; private Date annoProduzione; @ManyToMany(cascade=CascadeType.ALL, fetch=FetchType.LAZY, targetEntity=Autore.class) private Set<Autore> autori = null;...}

Osservando il codice della classe Prodotto è possibile no-tare l’uso delle annotazioni @Inheritance e @Discrimina-torColumn. Attraverso l’annotazione @Inheritance è possi-bile specificare la strategia di mapping, nell’esempio SIN-GLE_TABLE. L’annotazione @DiscriminatorColumn consen-te di specificare il nome, nell’esempio “DISC”, e il tipo del-la colonna che funge da discriminante nel distinguere le sottoclassi della catena gerarchica. Di seguito riportiamo i frammenti di codice significativi per le altre classi della ge-rarchia. Iniziamo con uno stralcio del codice sorgente del-la classe Libro:

@Entity(name=”Libro_Single”)

@DiscriminatorValue(“LIBRO”)public class Libro extends Prodotto {... private int numeroPagine; private boolean edizioneEconomica;...}

Ecco uno stralcio del codice della classe Video,

@Entity(name=”Video_Single”)public abstract class Video extends Prodotto {... private boolean colore;...}

e della classe VHS,

@Entity(name=”VHS_Single”)@DiscriminatorValue(“VHS”)public class VHS extends Video {... private String tipoNastro = null;...}

Page 40: j2007 04 jj5

JAVA Journal

n.5 - novembre/febbraio 200840

focus JAVA Journal

n.5 - novembre/febbraio 2008 41

focus

e, per concludere, della classe DVD:

@Entity(name=”DVD_Single”)@DiscriminatorValue(“DVD”)public class DVD extends Video {... private boolean contenutiSpeciali;...}

Osservando il codice sopra riportato è possibile notare come, al variare delle classi, varia il valore della colonna “discriminante” (nell’esempio la colonna “DISC”). Il va-lore che assume in base al tipo della classe è specificato at-traverso l’annotazione @DiscriminatorValue. In particolare, la colonna “DISC” assume il valore “LIBRO” in presenza di un record che rappresenta un oggetto della classe Libro e, viceversa, assume il valore “VHS” in presenza di un video del tipo VHS ed il valore “DVD” in presenza di un record che rappresenta un oggetto DVD. Dopo aver definito il modello ad oggetti e dopo aver dichia-rato la strategia di mapping per l’ereditarietà, deriviamo la struttura delle tabelle del databa-se (processo di sviluppo top-down). Il provider JPA che usiamo è Hibernate il quale grazie alla proprietà:

hibernate.hbm2ddl.auto

impostata al valore “create” consen-te di derivare le tabelle del database partendo dalle classi opportunamen-te annotate. Il Listato 1 riporta il sor-gente del file persistence.xml. Il risultato del deploy delle classi entity in JBoss è la creazione delle tabelle per il databa-se MySql, riportate in Figura 2.

LISTATO 1 Il file persistence.xml

<persistence><persistence-unit name=”GestioneBiblioteca”> <jta-data-source>java:/theDataSource</jta-data-source> <provider>org.hibernate.ejb.HibernatePersistence</provider> <class>it.articolo.jpa2.Prodotto</class> <class>it.articolo.jpa2.Libro</class> <class>it.articolo.jpa2.Video</class> <class>it.articolo.jpa2.VHS</class> <class>it.articolo.jpa2.DVD</class> <class>it.articolo.jpa2.Autore</class> <class>it.articolo.jpa2.Indirizzo</class> <exclude-unlisted-classes/> <properties> <property name=”hibernate.show_sql” value=”true”/> <property name=”hibernate.format_sql” value=”true”/> <property name=”hibernate.hbm2ddl.auto” value=”create”/> </properties></persistence-unit></persistence>

Osservando la Figura 2 possiamo notare come la strategia SINGLE_TABLE abbia effettivamente portato alla genera-zione di una singola tabella, nell’esempio la tabella PRO-DOTTO_SINGLE, le cui colonne sono tutti gli attributi del-le classi costituenti la gerarchia, più la colonna discrimi-nante dichiarata attraverso l’annotazione @Discrimina-torColumn, nell’esempio la colonna “DISC”. Osserviamo nel dettaglio la struttura della tabella PRODOTTO_SIN-GLE. Di seguito riportiamo il comando DDL (Data Defina-tion Language) della tabella:

CREATE TABLE PRODOTTO_SINGLE ( DISC varchar(31) NOT NULL, id int(11) NOT NULL auto_increment, titolo varchar(255), annoProduzione datetime, numeroPagine int(11), edizioneEconomica bit(1), colore bit(1), tipoNastro varchar(255), contenutiSpeciali bit(1), PRIMARY KEY (id))

FIGURA 2 Il diagramma ER (strategia di mapping SINGLE_TABLE)

Page 41: j2007 04 jj5

JAVA Journal

n.5 - novembre/febbraio 200840

focus JAVA Journal

n.5 - novembre/febbraio 2008 41

focus

FIGURA 3 Il diagramma ER (strategia di mapping JOINED)

A parte la colonna “DISC” e la colonna ID che è la chiave primaria, tutte le altre colonne della tabella possono assumere il valore NULL. Questo è necessario, in quanto un record del-la tabella può rappresentare una qualsiasi classe presente nella gerarchia. Se, per esempio, avessimo mappato l’attri-buto “contenutiSpeciali” della classe DVD su una colonna dichiarata come NOT NULL sarebbe stato impossibile per-sistere un oggetto delle classi Libro e VHS…

La strategia JOINED

Analizziamo ora la strategia di mapping di tipo JOINED. A livello di codice Java le classi Prodotto, Libro, Video, VHS e DVD non variano, a parte il valore assegnato alla anno-tazione @Inheritance. Di seguito uno stralcio del sorgente delle classe Prodotto e Libro:

@Entity(name=”Prodotto_Joined”)@Table(name=”PRODOTTO_JOINED”)@Inheritance(strategy=InheritanceType.JOINED)public abstract class Prodotto {...}

@Entity(name=”LIBRO_JOINED”)public class Libro extends Prodotto {...}

La struttura delle tabelle che deriva dal modello delle clas-si è riportata in Figura 3. Per ogni entità della gerarchia è generata una tabella le cui colonne sono solo gli attributi esplicitamente dichiarati nella classe. La root class della ge-rarchia è mappata su una root table (in Figura 3 la tabella PRODOTTO_JOINED) nella quale è definita la chiave pri-maria poi usata in tutte le tabelle della gerarchia. Ogni ta-bella nella gerarchia definisce una chiave primaria uguale alla chiave primaria della root table e definisce una foreign key (chiave esterna) verso la chiave primaria della root ta-ble.

Considerazioni preliminari sulle strategie di mapping

Quale strategia di mapping applicare ad una gerarchia di classi? La risposta a questa domanda non è semplice, in quanto varia in funzione del risultato che si desidera otte-nere: per esempio, prestazioni adeguate o maggiore fles-sibilità durante le fasi di design e/o deployment. Di seguito cercheremo di delineare alcune linee guida, ben consci che tali indicazioni sono preliminari e che necessitano di una sperimentazione sul campo, specifica al proprio contesto.

Applicazione della strategia SINGLE_TABLE

La strategia di mapping SINGLE_TABLE è consigliata in presenza di una gerarchia stabile e sostanzialmente sem-plice e poco “profonda”. Infatti aggiungere una nuova classe alla gerarchia o aggiungere nuovi attributi a qualche classe preesistente nella gerarchia porta alla creazione di nuove colonne nella tabella di mapping. Pertanto una ge-rarchia molto estesa può condurre alla generazione di una tabella con un numero di colonne elevato con la conseguenza di un impatto negativo sul “layout” della tabella e del data-base in generale. Sempre in relazione al “layout” della ta-bella di mapping è opportuno ricordare che le colonne del-la tabella mappate sugli attributi delle classi della gerar-chia devono poter assumere il valore NULL. Inoltre, se per qualche motivo vi è la necessità di mappare uno o più attri-buti in colonne di tipo LOB può essere necessario mappare l’entity class non più su una sola tabella ma su più tabelle attraverso l’annotazione @SecondaryTable. Relativamen-te alle prestazioni, poiché generalmente si è in presenza di una singola tabella, le operazioni di ricerca sui diversi tipi della gerarchia o su uno specifico tipo risultano efficienti, non essendo richieste operazioni di JOIN tra tabelle.

Page 42: j2007 04 jj5

JAVA Journal

n.5 - novembre/febbraio 200842

focus JAVA Journal

n.5 - novembre/febbraio 2008 43

focus

Applicazione della strategia JOINED

La strategia di mapping di tipo JOINED offre una buona flessibilità durante le fasi di design e di deployment. Infat-ti, l’aggiunta o la rimozione di uno o più attributi ad una classe presente nella gerarchia coinvolge nella modifica solo la tabella che specificatamente rappresenta la classe; mentre l’introduzione di una nuova classe nella gerarchia porta semplicemente alla creazione di una nuova tabella nel-lo schema. Relativamente alle prestazioni, una operazione di ricerca tra le entità della gerarchia necessiterà di una o più operazioni di JOIN anche in funzione della “profon-dità” della gerarchia. Pertanto la presenza di un numero elevato di sottoclassi può avere un impatto negativo sulle prestazioni.

Una API per le query con JPA

La specifica JPA definisce un linguaggio per eseguire le query sulle entità ed il loro stato persistente. Il linguag-gio consente allo sviluppatore di specificare la semantica di una query in modo portabile ed indipendente dallo spe-cifico database presente nella propria infrastruttura tecno-logica. Con JPA si è in presenza di una radicale estensione del linguaggio EJB QL definito nella precedente specifica per gli EJB di tipo Entity Bean versione 2.1 [5].Nel precedente articolo su JPA [1] abbiamo osservato come attraverso l’oggetto EntityManager è possibile fare semplici operazioni di ricerca attraverso il metodo find(). La firma del metodo è:

public <T> T find(Class<T> entityClass, Object primaryKey);

Il metodo accetta come parametro di input il tipo della en-tity class di cui tornarne una istanza ed un oggetto che rap-presenta la chiave primaria e restituisce l’istanza della en-tity class di data chiave primaria. Il metodo è pratico e allo stesso tempo semplice. Tuttavia nei progetti è necessario, la maggior parte delle volte, eseguire query complesse la cui esecuzione restituisce, eseguendo operazioni di JOIN e condizioni di WHERE articolate, una o più istanze di enti-ty class. In questi casi è necessario usare un oggetto di tipo Query, definito nel package javax.persistence e la sintassi estesa del linguaggio EJB QL. I passi essenziali per creare un oggetto Query ed eseguire una query sono schematizza-ti di seguito:

• Recuperare dall’Entity Manager una istanza della classe javax.persistence.Query;• Impostare i parametri necessari all’esecuzione della query;• Eseguire effettivamente la query.

Come si è detto in precedenza, le query possono essere scritte attraverso il linguaggio EJB QL. Tuttavia è anche possibile usare direttamente il linguaggio SQL ed in tal caso si è in presenza di query scritte in linguaggio nativo (indicate, infatti, con il termine native query nella specifi-ca). Il linguaggio EJB QL, sintatticamente molto simile al linguaggio SQL, è la via preferenziale per scrivere le query

in JPA. Infatti, il linguaggio è object-oriented, usando nella sua sintassi le entity class e le regole di navigazione definite tra le entità, ed è indipendente dal database. Al contrario, il linguaggio SQL, pur esistendo uno standard, è di fatto coniugato in diversi “dialetti” caratteristici di ogni data-base che non ne garantiscono la portabilità (vendor lock-in). Per ottenere una istanza dell’oggetto Query è possibile in-vocare sull’oggetto EntityManger o il metodo createQuery() o il metodo createNativeQuery(). Il primo metodo usa il lin-guaggio EJB QL per la definizione dello statement mentre il secondo metodo usa il linguaggio SQL. Per esempio, con riferimento alla Figura 2, per ottenere tutti i prodotti è possibile scrivere:

public List<Prodotto> getProdotti(){ Query query = entityManager.createQuery(“SELECT o FROM Prodotto_Single o”); return query.getResultList();}

L’espressione che rappresenta lo statement di una query può essere ricavata a runtime, ossia durante l’esecuzione del programma, come di fatto accade nell’esempio prece-dente; o può essere dichiarata in un file di configurazione della applicazione. Nel primo caso si è in presenza di una query dinamica, nel secondo di una query statica o di una na-med query. Le query statiche sono dichiarate attraverso la annotazione @NamedQuery. Per esempio, la query prece-dente può divenire una named query dichiarandola come annotazione del sorgente della classe Prodotto:

@Entity(name=”Prodotto_Single”)...@NamedQuery(name = “Prodotto.findAll”, query = “SELECT o FROM Prodotto_Single o”)public abstract class Prodotto implements Serializable {...}

Per usare una named query il metodo precedente diviene:

public List<Prodotto> getProdotti(){ Query query = entityManager.createNamedQuery( “Prodotto.findAll”); return query.getResultList();}

Ovviamente è possibile dichiarare più di una named query come annotazione di una entity class. In tal caso è neces-sario usare l’annotazione @NamedQueries. Prima di ter-minare è opportuno accennare ai named parameters attra-verso i quali è possibile specificare le condizioni di WHE-RE. Per esempio se vogliamo ricercare i prodotti che han-no un titolo particolare possiamo definire la named query

Page 43: j2007 04 jj5

JAVA Journal

n.5 - novembre/febbraio 200842

focus JAVA Journal

n.5 - novembre/febbraio 2008 43

focus

come segue:

@NamedQuery(name = “Prodotto.findByTitolo”, query = “SELECT o FROM Prodotto_Single o WHERE o.titolo = :TITOLO”)

ed un metodo che la usa:

public List<Prodotto> getProdottiByTitolo(String titolo){ Query query = entityManager.createNamedQuery( “Prodotto.findByTitolo”); query.setParameter(“TITOLO”, titolo); return query.getResultList();}

EJB-QL: nuove caratteristiche

In precedenza abbiamo osservato come il linguaggio per eseguire le query definito nella specifica JPA sia una esten-sione del linguaggio EJB QL. In particolare, introduce varie migliorie che di seguito accenniamo:

• Aggiornamenti e cancellazioni “massive”;• Operazioni di JOIN;• Parole chiave GROUP BY e HAVING;• Projection;• Subquery.

Operazioni massive

Per aggiornamenti e cancellazioni “massive” indichiamo una singola operazione il cui risultato è l’eliminazione o aggiornamento di un numero, anche elevato, di entità (in inglese i termini per indicare queste caratteristiche sono bulk update e bulk delete). Quando si esegue una operazione di cancellazione o di aggiornamento “massivo” valgono le seguenti regole:

• L’operazione “massiva” si applica all’entità “sorgente” della bulk operation e a tutte le sottoclassi di questa;• L’operazione non si propaga alle entità relazionate con l’entità “sorgente” della bulk operation;• Il persistence context non è sincronizzato con il risultato della operazione.

L’ultima tra le regole sopra elencate merita una riflessione. Poiché il persistence context non si sincronizza con il risulta-to di una operazione “massiva” è necessario che tali opera-zioni avvengano o all’inizio di una transazione o che siano eseguite in una transazione separata. Lo stralcio di codice che segue (due metodi presenti in un EJB di tipo session stateless) mostra un semplice esempio di bulk update e di bulk delete:

@TransactionAttribute( TransactionAttributeType.REQUIRES_NEW)public void bulkUpdate(String entityName){ String dOperation = “UPDATE “+entityName+” t SET t.annoProduzione = :ANNO_PRODUZIONE”; Query q = entityManager.createQuery(dOperation); q.setParameter(“ANNO_PRODUZIONE”, null); q.executeUpdate();} @TransactionAttribute( TransactionAttributeType.REQUIRES_NEW)public void bulkDelete(String entityName){ Query q = entityManager.createQuery( “DELETE FROM “+entityName); q.executeUpdate();}

In particolare, con riferimento alla Figura 2, il metodo bulkUpdate() imposta il valore della colonna “anno di pro-duzione” presente nella tabella PRODOTTO_SINGLE al va-lore null; mentre il metodo bulkDelete() elimina tutti i re-cord rappresentati da una entity class. Osserviamo che se si invocato i metodi bulkUpdate() e bulkDelete() passando come parametro di input il valore “Libro_Single”, ossia il nome della entity class Libro, sono aggiornati ed eliminati i record relativi ad un libro; viceversa, se si invocano i due metodi passando come parametro di input il valore “Pro-dotto_Single”, ossia il nome della entity class Prodotto (la root class della gerarchia), sono aggiornati ed eliminati tutti i record relativi ad un libro, ad un video di tipo DVD e ad un video di tipo VHS.

Operazioni di JOIN

Analizziamo ora il supporto introdotto in JPA per la gestio-ne delle operazioni di JOIN. Con JPA possiamo avere i se-guenti tipi di JOIN:

• JOIN;• LEFT JOIN;• FETCH JOIN.

Per esempio, sempre con riferimento alla Figura 2, se vo-lessimo trovare tutti i prodotti che hanno associato almeno un autore potremmo scrivere la JOIN:

@NamedQuery(name=”prodottiByAutoreJOIN”, query=”SELECT o FROM Prodotto_Single o JOIN o.autori”)

Viceversa, per ottenere i prodotti ai quali è associato o meno un autore scriveremo la LEFT JOIN:

@NamedQuery(name=”prodottiByAutoreLEFT_JOIN”, query=”SELECT o FROM Prodotto_Single o LEFT JOIN o.autori”)

Page 44: j2007 04 jj5

JAVA Journal

n.5 - novembre/febbraio 200844

focus

Le FETCH JOIN consentono di caricare le entità correlate specificate in una query, indipendentemente dalla politica di “loading” specificata nella definizione della relazione. Ricordiamo che in JPA, nella definizione di una relazione, la politica di caricamento può assumere i valori: EAGER e LAZY (Per ulteriori informazioni si veda [1]). La modalità di caricamento EAGER è generalmente meno efficiente, in quanto porta a caricare in memoria l’intera mappa delle re-lazioni; viceversa, la modalità di caricamento LAZY indica che le entità correlate sono caricate solo quando è necessa-rio (purché vi sia un persistence context attivo). Ad esempio (sempre con riferimento alla Figura 2), se tra le entità Pro-dotto e Autore la relazione molti a molti fosse caratterizzata da una modalità di caricamento LAZY e volessimo esegui-re una query il cui risultato siano i prodotti con valorizzato l’elenco di autori ad essi relazionati potremmo scrivere:

@NamedQuery(name=”prodottiByAutoreFETCH_JOIN”, query=”SELECT o FROM Prodotto_Single o JOIN FETCH o.autori”)

Parole chiave GROUP BY e HAVING

Nella precedente specifica di EJB QL era assente il suppor-to per le parole chiave GROUP BY e HAVING di SQL. Nella attuale specifica JPA tale supporto è stato introdotto: per-tanto è possibile scrivere query che specificano raggruppa-menti e condizioni tramite queste keyword. Per esempio per ottenere i conteggi del numero di video, sia VHS che DVD, che sono a colori ed in bianco e nero possiamo scri-vere:

@NamedQuery(name=”contaVideo”, query=”SELECT o.colore, COUNT(o) FROM Video_Single o GROUP BY o.colore”)

Proiezioni

Con il termine Projection la specifica indica la possibilità che nel risultato di una query siano presenti solo alcuni at-tributi di una entità o di un insieme di entità. Si tratta di una ottimizzazione nel caso ad un client non interessi la-vorare con la totalità degli attributi di una entità, ma solo con un sottoinsieme. Per esempio, se avessimo necessità di un elenco con solo i titoli e l’anno di produzione dei pro-dotti possiamo scrivere:

@NamedQuery(name=”findWithProjection”, query=”SELECT o.titolo, o.annoProduzione FROM Prodotto_Single o”)

Il risultato della query è un vettore in cui ogni elemento è un array di Object. Nell’esempio riportato, ogni array di Object ospitato nel vettore contiene due elementi: il titolo di tipo String e l’anno di produzione di tipo Date.

Subquery

Concludiamo questa panoramica sulle nuove caratteristi-che introdotte nel linguaggio parlando delle subquery. At-traverso una subquery è possibile eseguire delle query come parte della condizione di WHERE. L’uso delle subquery deve essere ben valutato in quanto può portare ad un degrado delle prestazioni. Ad esempio, se volessimo recuperare tut-ti i prodotti che sono stati realizzati da almeno due autori è possibile scrivere:

@NamedQuery(name=”findWithSubQuery”, query=”SELECT o FROM Prodotto_Single o WHERE (SELECT count(e) FROM o.autori e) > 0”)

Conclusioni

Con questo articolo concludiamo la panoramica sulla spe-cifica JPA. Non è stato possibile trattare tutte le caratte-ristiche della persistence API, tuttavia sono evidenti due peculiarità: da un lato la semplicità di JPA come prodotto ORM (Object Relational Mapping) e dall’altro l’estrema fles-sibilità dell’API con il mapping delle relazioni e della ere-ditarietà. Sarà il tempo a dirci se JPA diverrà la modali-tà standard e più diffusa, tra sviluppatori e architetti del software, per persistere gli oggetti in Java. Tuttavia, per quanto si è detto e per la diffusione di Hibernate e TopLink (genitori naturali di JPA), le premesse sono estremamen-te positive.

Riferimenti

[1] : JPA (prima parte). Java Journal N. 4, anno I.[2]: http://www.jboss.com/.[3]: http://www.mysql.com/[4]: http://www.hibernate.org/[5] : Enterprise JavaBeans, v 2.1. http://java.sun.com/products/ejb

Bibliografia:

[MEJ]: Mastering Enterprise JavaBeans 3.0, edito dalla Wiley Publishing, Inc. Autori: Rima Patel Sriganesh, Gerald Bro-se, Micah Silverman.[BAD]: Beginning EJB 3 Application Development, edito dalla Apress. Autori: Raghu R. Kodali and Jonathan Wetherbee with Peter Zadrozny

Note Biografiche

Fabio Staro dottore in Fisica è Responsabile Tecnico per i progetti Java presso la Direzione Ricerca ed Innovazione diEngineering Ingegneria Informatica S.p.A.

Page 45: j2007 04 jj5
Page 46: j2007 04 jj5

SPEECH Login TopicFREE

BB: Il JUG si occupa principalmente di diffondere la co-noscenza di Java sul territorio locale organizzando meeting mensili e altri eventi, come il JavaDay e l’ancora più recente JavaDay@school [4], quando ci siamo recati ad evangelizzare (si fa per dire!) direttamente l’ITIS Avogadro di Torino :) Scri-viamo saltuariamente anche del software, qualcuno di noi parte-cipa a progetti opensource, ma quello che ci piace di più è con-dividere la conoscenza che ciascuno di noi possiede! Nei mee-ting troverete sempre interventi interessanti, da relatori com-petenti, e su argomenti che possono veramente fare da traino alla vostra formazione personale. Il JUG offre anche degli scon-ti agli iscritti per acquisire la certificazione Sun e per partecipa-re a corsi di formazione. Inoltre organizza ogni tanto “trasferte” per seguire le conferenze internazionali più importanti, come JavaPolis [5] o Jazoon [6].

JJ: Torino rappresenta uno dei poli scientifici ed industriali più impor-tanti del Paese. Il JUG, per così dire, nasce in una valle fertile ...

BB: Sì, anche se trovandoci in un’area industriale ci ritrovia-mo comunque di fronte a una mentalità analoga, con processi di tipo predittivo e strumenti “mainstream”. Qui le industrie usa-no ancora Struts 1 e i DAO, e non è sempre uno spasso. Il van-taggio è che lo spazio per crescere è molto, e i membri del JUG sono spesso dei “motori” di questa spinta!

Dopo aver conosciuto il Java User Group Sardegna ONLUS, proseguiamo il nostro viaggio nella penisola alla scoperta dei JUG italiani. Dal sud a nord: facciamo conoscenza con il JUG Torino e con Bruno Bossola, Java Champion nonché uno dei fondatori del gruppo piemontese.

Java Journal: Prima di tutto, ci dici in due parole chi è Bruno Bos-sola?

Bruno Bossola: Sono uno dei tanti programmatori in circola-zione che ha iniziato con un VIC20, percorrendo tutta la lunga strada CBM fino a chiudere con un fantastico Amiga 500 con 20MB di hard disk SCSI! Il mondo del lavoro mi ha portato a lavorare in C per 8 anni, prima nell’ambito dell’automazio-ne industriale e poi nel CAD, per poi proseguire con Java per i restanti 11 anni (wow, quanto tempo!). La mia dedizione alla piattaforma Java, unita anche a tutti gli sforzi che ho fatto per diffonderla, mi è anche valsa il riconoscimento di Java Cham-pion [1]

JJ: Com’è iniziata l’avventura del JUG Torino?

BB: L’idea originale è di Luigi Viggiano [2], che per primo in-viò la richiesta di un JUG sull’allora sito di mamma Sun. Quan-do tornai da un paio d’anni di vita a Milano io e Luigi, grazie anche all’aiuto di altre persone, fondammo in un bar, di fronte a una sontuosa esposizione alcolica :) , il JUG Torino. Tutta la sto-ria la trovate comunque sul sito del JUG [3]

JJ: Quali sono le attività principali del JUG?

COMMUNITY

n.5 - novembre/febbraio 200846

COMMUNITY WATCHIl JUG TorinoÜ di Stefano Sanna ([email protected])

Note Biografiche

Stefano Sanna si occupa di sviluppo di applicazioni multi-mediali per piattaforma Savaje presso Beeweeb Technolo-gies (http://www.beeweeb.com). Ha lavorato per sette anni nel gruppo Network Distributed Applications del CRS4, oc-cupandosi di mobile computing e applicazioni network-orien-ted per cellulari e PDA. Collabora attivamente con JIA, JUG Sardegna e Java Mobile Developers Forum. Il suo sito web personale è http://www.gerdavax.it

Page 47: j2007 04 jj5

SPEECH Login TopicFREE COMMUNITY

JJ: In Italia ci sono tanti JUG, apparentemente segno di divisione. In realtà i JUG italiani collaborano tanto tra di loro, il JavaDay ne è pro-va tangibile. Perché?

BB: Il territorio italiano purtroppo non si presta ad avere un singolo JUG, la sua stessa struttura porta ad una forte segmen-tazione. Ecco perché ci sono tanti JUG, che però collaborano attivamente fra di loro: la motivazione alla base della separa-zione è tutt’altro che campanilistica. In effetti però allo stato attuale la frammentazione è un po’ troppa, e quello che insie-me con il JUG Milano si sta cercando di fare, ad esempio qui al nord, è di trovare una squadra per formare almeno un’enti-tà che aggreghi noi due (e chi si vuole unire!) anche perché il coordinamento delle attività del JUG pesa parecchio. Comun-que ben vengano anche JUG microscopici: quando c’è la voglia sei al 90% dell’opera :) , e la volontà di fare cose tutti insieme non manca mai!

JJ: Java ha portato con sé tante tecnologie che hanno rivoluzionato il modo di programmare (Applet, MIDlet, Servlet ...). Quale sarà, a tuo avviso, la prossima rivoluzione?

BB: La prossima rivoluzione per quanto riguarda la piattafor-ma Java si svolgerà nel mondo mobile: nuovi e potenti devi-ce, con processori di potenza paragonabile a quella degli attua-li desktop, renderanno possibile operare con API simili a quelli di Java SE, rendendo di fatto obsoleta l’attuale distinzione fra queste piattaforma e Java ME; alla fine queste si unificheran-no, differenziandosi magari solo per un profilo. In generale nel-l’arena dei linguaggi prevedo un’ascesa di Javascript, che nei prossimi anni diventerà pienamente object oriented e rivaleg-gerà a pieno titolo la supremazia attuale di Java.

JJ: Quali, invece, le limitazioni che Java dovrebbe superare?

BB: Java dovrebbe innanzitutto lavorare su alcune librerie di base che attualmente incorpora nel core, cercando di eliminar-le o riscriverle. Innanzitutto il Collections framework: una cosa veramente triste; viola pressoché tutti i principi OO che cono-sco (pensate ad esempio alla sostituibilità e alle famigerate Un-supportedOperationException); soffre di una progettazione ve-ramente povera (pensate ai tristissimi iteratori esterni: ma i blocchi di SmallTalk proprio li abbiamo dimenticati?); e man-ca anche secondo me di una metafora uniforme (è così difficile pensare alle liste come delle mappe in cui la chiave è l’indice?). Passiamo ora a Swing: a me sembra un campionato di overdesi-gn! Insomma, alla fine dobbiamo aprire delle finestre con den-

tro campi e bottoni: non ci potete dare un’infrastruttura di base molto buona (pensate a JDBC ad esempio) mentre poi qualcun altro si occupa, eventualmente, di creare un framework decen-te? (pensate a Hibernate ora). Questo comunque per citarne un paio; se volete approfondiamo: sul lato EE ci sono ben altri mo-stri, o anche roba che semplicemente non serve, tanto che anche la Sun se ne è accorta e per la prossima Java EE 6 comincerà con il cosiddetto “pruning” di alcune librerie (ciao ciao EJB2 enti-ty beans!!!). Per quanto riguarda il linguaggio ci sono due passi ancora fondamentali da fare secondo me. Innanzitutto allegge-rire il controllo sui tipi, che nacque per aiutare il programma-tore a fare meno errori ma che ora è diventato semplicemente un modo per imboccare il compilatore: noi abbiano bisogno che il nostro codice sia espressivo, che dichiari le sue intenzioni, mentre siamo costretti a irrigidirci in costrutti sintattici molto complessi che sono di solo aiuto al compilatore, non a noi. Ri-tengo poi necessario introdurre le closure, meglio con la propo-sta BGGA di Gafter & Co, che permetterebbero di realizzare au-tonomamente semplici estensioni al linguaggio e di introdurre un approccio innovativo allo sviluppo, consentendo ad esempio con molta semplicità l’uso di iteratori interni.

JJ: Un consiglio al neofita che vuole imparare il linguaggio...

BB: Beh, libri che parlano di Java ce ne sono parecchi, ma per quanto riguarda i neofiti devo dire che non ho ancora trovato un testo in italiano decoroso ... Onestamente non saprei consi-gliare! Forse “Thinking in Java”, di Eckel oppure “Core Java” di Horstmann, che si trovano anche tradotti in italiano. Il mio consiglio è di imparare prima di tutto l’inglese e di acquista-re qualche libro in lingua originale, che parli di Object Orien-ted, come “Object-Oriented Software Construction” di Meyer. Quindi leggere un buon testo su Java, seguendo contempora-neamente il Java Tutorial [7] sul sito della Sun. Se avete la possi-bilità poi di seguire un corso fatelo, se poi il docente è bravo an-cora meglio. Non dimenticatevi poi di iscrivervi ad un JUG lo-cale: quello sarà per voi il vero punto di riferimento nel tempo!

JJ: JUG Torino nel 2008: cosa bolle in pentola?

BB: Per il 2008 prevediamo sicuramente di replicare il JavaDay, visto il successo dell’iniziativa (più di 130 persone sono interve-nute nel 2007!). E credo cercheremo di portare avanti il proget-to JavaDay@school coinvolgendo altre scuole. Insieme con gli altri JUG, inoltre, stiamo poi cercando di organizzare una nuo-va iniziativa nazionale, sulla quale non posso purtroppo anco-ra dire nulla ma che vi garantisco sarà qualcosa di sensazionale! Rimanete sintonizzati sui nostri blog :) [8]

Ringraziamo Bruno Bossola per il tempo che ci ha dedicato e per l’impegno suo e di tutto il JUG Torino nel promuovere Java nel nostro Paese.

[1] http://java-champions.dev.java.net/[2] http://it.newinstance.it/[3] http://www.jugtorino.it/vqwiki/jsp/Wiki?StoriaDelJug[4] http://www.jugtorino.it/vqwiki/jsp/Wiki?JavaDayAtSchool[5] http://www.javapolis.com[6] http://www.jazoon.com[7] http://java.sun.com/docs/books/tutorial/[8] http://www.jugtorino.it/vqwiki/jsp/Wiki?Blog

n.5 - novembre/febbraio 2008 47

Page 48: j2007 04 jj5
Page 49: j2007 04 jj5

ON-SITE

CORSIDI FORMAZIONE

ON-SITE

[email protected]

Page 50: j2007 04 jj5

OFFERTE ABBONAMENTI

INDIRIZZO DI SPEDIZIONE

Nome/Società __________________________________________________________

Codice Fiscale (obbligatorio) ________________________________________________

Indirizzo _______________________________________________________________

CAP _____________ Città ___________________________________ Prov. ______

Telefono ____________________________ Fax ______________________________

E-Mail ________________________________@_______________________________

Codice Cliente __________________________________________________________

MODALITA’ DI PAGAMENTO� Allego fotocopia del Bonifico Bancario effettuato sul C/C 000000014804

CAB 25200 ABI 01030 Cin “A” - Monte dei Paschi di Siena - Agenzia di Perignano IBAN IT84A0103025200000000014804

� Allego fotocopia della ricevuta del versamento sul C/C Postale N.14291561 intestato a: “Gruppo Editoriale Infomedia S.r.l. - Ponsacco” IBAN IT84X0760114000000014291561

� Allego assegno bancario intestato a: “Gruppo Editoriale Infomedia S.r.l.” NON TRASFERIBILE

� Contrassegno (+ € 11.00 contributo spese postali)

� Autorizzo l’addebito dell’importo di € _____________________ sulla mia CARTASI’/VISA

N. _________________________________________________________________

Scad. ______ / ______ (mm/aa) Codice di sicurezza “CV2” ______________

Nome del Titolare ____________________________________________________

Data di nascita ____________________________________________________

Firma del Titolare ____________________________________________________

� Collegati al sito www.shopinfomedia.it dove potrai effettuare il pagamento con carta di credito in modalità sicura (banca SELLA)

SI, mi voglio iscrivere gratuitamente alla newsletter Infomedia che mi tiene aggiornato sui prossimi argomenti delle mie riviste di programmazione e mi informa delle offerte, le nuove uscite e le prossime pubblicazioni. Questo è il mio indirizzo di posta elettronica:

______________________________@____________________________

Posso annullare la mia iscrizione in qualsiasi momento alla pagina: www.infomedia.it/newsletter.htm

PRIVACY Con la presente si autorizza al trattamento dei dati personali ai sensi delle vigenti norme sulla privacy

FIRMA________________________________________________

Garanzia di riservatezza - Gruppo Editoriale Infomedia garantisce la massima riservatezza dei dati da lei forniti e la possibilità di richiederne gratuitamente la rettifica o la cancellazione scrivendo a: Responsabile Dati - Gruppo Editoriale Infomedia Srl - Via Valdera P. 116 - 56038 Ponsacco (PI). Le informazioni custodite nel nostro archivio elettronico verranno trattate in conformità alla legge 196/03 sulla tutela dati personali.L’IVA sul prezzo dell’abbonamento cartaceo è assolta dall’Editore e non sussiste l’obbligo di emissione della fattura, ai sensi del D.M. 9 aprile 1993, art.1, comma 5; pertanto, ai fini contabili, farà fede la sola ricevuta di pagamento; perciò la fattura verrà emessa solo se esplicitamente richiesta al momento dell’ordine.Lei può dedurre il costo dell’abbonamento dal reddito d’impresa e dai redditi derivanti dall’esercizio di arti e professioni (artt. 54 e 56 del TUIR)

• I prezzi degli abbonamenti Web sono conprensivi dell’ IVA al 20%

• I prezzi degli abbonamenti per l’estero sono maggiorati di spese di spedizione.

• Gli abbonamenti decorrono dal primo numero raggiungibile. Per attivazioni retroattive contattaci al numero 0587/736460 o invia una e-mail all’indirizzo: [email protected]

INDIRIZZO DI FATTURAZIONE (solo per riviste web)

Nome e Cognome _______________________________________________________

Ditta __________________________________________________________________

Indirizzo _______________________________________________________________

CAP ____________ Città _________________________________ Prov. _________

P.IVA _________________________________________________________________

E-Mail per invio fattura _________________________ @ ________________________

GRUPPO EDITORIALE INFOMEDIA S.R.L.Via Valdera P. 116 - 56038 Ponsacco (PI) - Tel. 0587 736460 - Fax 0587 732232

www.infomedia.it - [email protected]

2008

WEB Cartaceo COMPUTER PROGRAMMING 50,00€ 75,00€

DEV 50,00€ 75,00€

VISUAL BASIC & .NET JOURNAL 40,00€ 52.50€

LOGIN 40,00€ 52.50€

JAVA JOURNAL 40,00€ 52.50€

2 a scelta 80,00€

3 a scelta 120,00€

4 a scelta 170,00€

5 a scelta 220,00€

ABBONAMENTO ANNUALE

Page 51: j2007 04 jj5

solo12,90 Euro

“Si fa presto a dire sito web”di Paolo Frattini

Anno 2007

54 pagine a colori, formato A4

ISBN 9788881500222

Ordinalo su

www.shopinfomedia.com

“Si fa presto a dire sito web”

Fai il make-up al tuo sito webcon l’ultimo libro di Paolo Frattini

Scopri regole, trucchi e consigli

per comunicare via Internet

Page 52: j2007 04 jj5