Sei sulla pagina 1di 5

DEV DEVeloping Software Solutions n.

117 aprile 2004

Generatore di classi per il pattern DAO


di Carlo Camusso
` ` ` Scrivere codice puo essere unattivita stimolante, ma farlo fare ad un programma e senza dubbio divertente

Carlo Camusso Si occupa di soluzioni enterprise per la Pubblica Amministrazione e le ` Unita Sanitarie Locali .

pubblicato su WWW.INFOMEDIA.IT stampa digitale da Lulu Enterprises Inc. stores.lulu.com/infomedia


Infomedia
` Infomedia e limpresa editoriale che da quasi venti anni ha raccolto la voce dei programmatori, dei sistemisti, dei professionisti, degli studenti, dei ricercatori e dei professori dinformatica italiani. Sono pi` di 800 gli autori che hanno realizzato per le teu state Computer Programming, Dev, Login, Visual Basic Journal e Java Journal, molte migliaia di articoli tecnici, presentazioni di prodotti, tecnologie, protocolli, strumenti di lavoro, tecniche di sviluppo e semplici trucchi e stratagemmi. Oltre 6 milioni di copie distribuite, trentamila pagine stampate, fanno di questa impresa la pi` grande ed u inuente realt` delleditoria specializzata nel campo della a programmazione e della sistemistica. In tutti questi anni le riviste Infomedia hanno vissuto della passione di quanti vedono nella programmazione non solo la propria professione ma unattivit` vitale e un vero a divertimento. ` Nel 2009, Infomedia e cambiata radicalmente adottando ` un nuovo modello aziendale ed editoriale e si e organizzata attorno ad una idea di Impresa Sociale di Comunit` , a partecipata da programmatori e sistemisti, separando le attivit` di gestione dellinformazione gestite da un board a comunitario professionale e quelle di produzione gesti` te da una impresa strumentale. Questo assetto e in linea con le migliori esperienze internazionali e rende Infomedia ancora di pi` parte della Comunit` nazionale degli u a sviluppatori di software. ` Infomedia e media-partner di manifestazioni ed eventi in ambito informatico, collabora con molti dei pi` imporu tanti editori informatici italiani come partner editoriale e fornitore di servizi di localizzazione in italiano di testi in lingua inglese.

Limpaginazione automatica di questa rivista e realizzata al ` 100% con strumenti Open Source usando OpenOffice, Emacs, BHL, LaTeX, Gimp, Inkscape e i linguaggi Lisp, Python e BASH

For copyright information about the contents of DEV, please see the section Copyright at the end of each article if exists, otherwise ask authors. Infomedia contents is 2004 Infomedia and released as Creative Commons 2.5 BY-NC-ND. Turing Club content is 2004 Turing Club released as Creative Commons 2.5 BY-ND. Le informazioni di copyright sul contenuto di DEV sono riportate nella sezione Copyright alla ne di ciascun articolo o vanno richieste direttamente agli autori. Il contenuto Infomedia e 2004 Infomedia e rilasciato ` con Licenza Creative Commons 2.5 BY-NC-ND. Il contenuto Turing Club e 2004 Turing Club e rilasciato ` con Licenza Creative Commons 2.5 BY-ND. Si applicano tutte le norme di tutela dei marchi e dei segni distintivi. ` E in ogni caso ammessa la riproduzione parziale o totale dei testi e delle immagini per scopo didattico purch e vengano integralmente citati gli autori e la completa identicazione della testata. Manoscritti e foto originali, anche se non pubblicati, non si restituiscono. Contenuto pubblicitario inferiore al 45%. La biograa dellautore riportata nellarticolo e sul sito www.infomedia.it e di norma quella disponibi` le nella stampa dellarticolo o aggiornata a cura dellautore stesso. Per aggiornarla scrivere a info@infomedia.it o farlo in autonomia allindirizzo http://mags.programmers.net/moduli/biograa

Generatore di classi per il pattern DAO


di

Carlo Camusso > ccamusso@infomedia.it

Scrivere codice pu essere unattivit stimolante, ma farlo fare ad un programma senza dubbio divertente

o sviluppo di un applicativo enterprise prevede la realizzazione di una serie pi o meno numerosa di classi per laccesso ai dati. Queste, oltre a rappresentare le entit di un database, contengono tutte le query necessarie al corretto funzionamento di un applicativo. La scrittura di questo codice, pur non essendo unoperazione particolarmente difficile, richiede tempo per: a) scrivere i metodi che impacchetteranno i dati restituiti delle istruzioni SQL; b) correggere gli inevitabili errori sintattici; c) trovare e eliminare i bug che notoriamente affliggono un po tutte le applicazioni. Alla luce di queste considerazioni, sarebbe bello poter disporre di uno strumento informatico che, dopo aver letto la struttura del database, possa generare automaticamente tutto questo codice.

LISTATO 1

Esempio di una classe VO

package esempio.vo; public class VOPersona{ private Integer id=null; private String nome=null; private String cognome=null; public Integer getId(){ return id; } public String getNome(){ return nome; } public String getCognome(){ return cognome; } public void setId(Integer id){ this.id=id; } public void setNome(String nome){ this.nome = nome; } public void setCognome(String cognome){ this.cognome = cognome; } }

Cos un generatore
Un generatore un software in grado di scrivere codice. Esistono contesti particolari (come quello affrontato in questo articolo), dove possibile sollevare il programmatore dalla sua attivit di sviluppo delegando la stessa ad un programma. I vantaggi sono importanti: se il generatore funziona correttamente, la scrittura delle classi sar completata in pochi secondi e senza errori. Questo si traduce in un risparmio di tempo per il programmatore e in un risparmio economico per lazienda.

Cos il pattern DAO


Lutilizzo di questo pattern permette di nascondere al business layer i dettagli dellimplementazione del database, fornendo tutti i meccanismi di accesso e gestione dei dati. Questa soluzione progettuale ci permette di
>> 80

analizzare una delle situazioni nelle quali un generatore di codice risulta essere estremamente utile. Cerchiamo di comprendere pi in dettaglio di cosa stiamo parlando supponendo di voler gestire una tabella chiamata Persona, allinterno della quale si trovano le colonne: id, nome e cognome. Quello che vogliamo fare implementare il pattern DAO per poter utilizzare questa tabella allinterno di una generica applicazione. Tale pattern prevede la realizzazione di due classi per ciascuna tabella presente nella base dati: una chiamata VO (Value Object) e unaltra chimata DAO (Data Access Object). Prima di tutto vediamo come fatta la classe value object relativa alla tabella Persona (Listato 1). Come si pu notare, si tratta di un semplice Java bean nel quale ogni attributo dichiarato privato, accessibile tramite i metodi pubblici set() e get(), e rappresenta una colonna della tabella. Ogni value object viene gestito da un data access object, chiamato anche DAO. Nellesempio in questione la classe DAOPersona visibile nel Listato 2.
DEV > n. 117 aprile 2004

Advanced

LISTATO 2
Esempio di una classe DAO
package esempio.dao; import esempio.vo.VOPersona; import java.sql.*; import java.util.ArrayList ; public class DAOPersona{ public final static DAOPersona SINGLETON = new DAOPersona(); private DAOPersona(){} public Collection selectAll(Connection con){ ArrayList arrayList = new ArrayList(); String sql=select * from Persona; Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery(sql); while (rs.next()) { VOPersona voPersona=new VOPersona(); voPersona.setId(rs.getInteger(1)); voPersona.setId(rs.getString(2)); voPersona.setId(rs.getString(3)); arrayList.add(voPersona); } return arrayList; } public boolean insertVO(Connection con, VOPersona){ //dettagli omessi } public boolean modifyVO(Connection con, VOPersona){ //dettagli omessi } public boolean deleteVO(Connection con, VOPersona){ //dettagli omessi } public boolean selectByVO(Connection con, VOPersona){ //dettagli omessi } //dettagli omessi }

1) analisi delle tabelle; 2) generatore di VO; 3) generatore di DAO.

Apprendere il contenuto del database


Prima di poter creare una qualsiasi classe dobbiamo sapere quali sono le tabelle presenti nel database. Nel metodo startGenerate () (Listato 3), viene utilizzato il metodo getTables() della classe java.sql.DatabaseMetaData contenuta nella distribuzione standard del package Java. Il metodo in questione ritorna un oggetto ResultSet contenente i nomi di tutte le tabelle presenti nel database al quale si collegati tramite loggetto java.sql.Connection. Per ogni tabella ottenuta, dobbiamo prelevare e temporaneamente archiviare i nomi e i tipi di tutti i suoi attributi. Per far ci scriviamo il metodo boGenerate(), il cui compito

LISTATO 3
Metodi per generare i VO
public void startGenerate(java.sql.Connection con) throws Exception{ Connection con= DriverManager.getConnection(...); DatabaseMetaData dmd = con.getMetaData(); ResultSet rs = dmd.getTables(null, null, null,new String[]{TABLE}); while (rs.next()){ String tableName = rs.getString(3); boGenerate(con, tableName) } } public void boGenerate(java.sql.Connection con, String tableName) throws Exception{ Hashtable tableParamiter = new Hashtable(); DatabaseMetaData dmd = con.getMetaData(); ResultSet rs = dmd.getColumns(null, null, tableName, null); while (rs.next()){ String name = rs.getString(4).toLowerCase(); String type = rs.getString(6).toLowerCase(); tableParamiter.put(name, type); } voGenerate(tableName, tableParamiter); daoGenerate(tableName, tableParamiter); } public void voGenerate(String tableName, Hashtable tableParamiter)throws Exception{ PrintWriter out = new PrintWriter(new BufferedWriter (new FileWriter(VO+tableName+.java))); out.println(public class VO + tableName); out.println({); //PROPRIETA DELLA CLASSE Enumeration enu=tableParamiter.keys(); while(enu.hasMoreElements()){ String name=(String)enu.nextElement(); String type=(String)tableParamiter.get(name); type=typeTOJavaType(type); out.println(private +type+ +name+;); } //METODI DELLA CLASSE enu=tableParamiter.keys(); while(enu.hasMoreElements()){ String name=(String)enu.nextElement(); String type=(String)tableParamiter.get(name); type=typeTOJavaType(type); out.println(public void set+name+(+type+ +name+)); out.println({); out.println( this.+name+=+name+;); out.println(}); out.println(public +type+ get+name+()); out.println({); out.println( return +name+;); out.println(}); } out.println(}); out.close(); } private String typeTOJavaType(String type){ if (type.equalsIgnoreCase(counter)) return Integer; else if (type.equalsIgnoreCase(integer)) return Integer; else if (type.equalsIgnoreCase(varchar)) return String; return Object; }

Nonostante lindiscutibile semplicit del codice in esame, la possibilit da parte di uno sviluppatore di commettere errori durante la digitazione elevata (e lo ancor di pi se il numero di tabelle da dover gestire aumenta di numero o di complessit).

Quando usare un generatore


Un veloce sguardo a VOPersona e DAOPersona mette in rilievo un importante aspetto: laccoppiamento che esiste tra queste classi e la tabella PERSONA elevato. Lutilizzo di questo pattern lascia ben poco margine decisionale al programmatore che, a conti fatti, non deve far altro che scrivere del codice che da una parte rispecchi la struttura della tabella e dallaltra contenga le principali istruzioni SQL che potrebbero essere eseguite. Il fatto che un programmatore non abbia un ruolo particolarmente attivo nella progettazione di queste classi ci permette di capire che questo uno dei contesti ideali per un generatore di codice.

Esistono contesti particolari dove possibile sollevare il programmatore dalla sua attivit di sviluppo delegando la stessa ad un programma
In questo caso il generatore potrebbe sostituirsi allattivit dello sviluppatore analizzando la struttura delle tabelle di un database, e quindi scrivendo tutte le classi VO e DAO.

Un generatore per il pattern DAO


Il generatore di business object che vogliamo realizzare costituito da tre procedure distinte:
DEV > n. 117 aprile 2004

81 <<

Advanced

LISTATO 4
Metodi per generare i DAO
public void daoGenerate(String tableName, Hashtable tableParamiter) throws Exception{ PrintWriter out = new PrintWriter(new BufferedWriter( new FileWriter(DAO+tableName+.java))); out.println(import VO + tableName+;); out.println(public class DAO + tableName); out.println({); methodSelectAll(out, tableName); methodQueryResult(out, tableName); out.println(}); out.close(); } private void methodSelectAll(PrintWriter out, String tableName) throws SQLException{ out.println(public Collection selectAll (Connection con){); Collection collection=null;); try{); Statement stmt=con.createStatement();); out.println( ResultSet rs=stmt.executeQuery(\SELECT * FROM +tableName+\);); out.println( collection=this.getQueryResult(rs);); out.println( }); out.println( catch(java.sql.SQLException e){); out.println( e.printStackTrace();); out.println( }); out.println( return collection;); out.println(}); } private void methodQueryResult(PrintWriter out, String tableName){ out.println(private Collection getQueryResult(java.sql.ResultSet rs){); out.println(java.util.ArrayList collection = new java.util.ArrayList();); out.println(try{); out.println(while(rs.next()){); out.println( VO+tableName+ tmp = new VO+tableName+();); out.println( Class example = Class.forName(\VO+tableName+\);); out.println( java.sql.ResultSetMetaData md=rs.getMetaData();); out.println( int columnCount = md.getColumnCount();); out.println( for(int i=1; i<=columnCount; i++){); out.println( Class classType = Class.forName(md.getColumnClassName(i));); out.println( Class[] parameterType = new Class[] {classType};); out.println( String methodName = md.getColumnName(i).toLowerCase();); out.println( methodName = methodName.toUpperCase().substring(0,1) + methodName.substring(1);); out.println( java.lang.reflect.Method method = example.getMethod(\set\+methodName, parameterType);); out.println( Object[] argument = new Object[] {rs.getObject(i)};); out.println( Integer result = (Integer) method.invoke(tmp, argument);); out.println( }); out.println( collection.add(tmp);); out.println(}); out.println(closeResultSet(rs);); out.println(}); } out.println( out.println( out.println(

propriet di ciascuna classe. Bisogna sempre tenere presente che i tipi e le classi Java possono non corrispondere sintatticamente a quelle del database. Conviene, allora, munirsi di un adattatore di sintassi che permetta al generatore di trovare il tipo pi adatto per trattare quello presente in una tabella. Ad esempio, dobbiamo assicurarci che tutte le colonne di tipo varchar contenute nelle tabella del database vengano trattate come java.lang.String allinterno delle classi Java. Per far questo utilizzeremo il nostro metodo typeTOJavaType() (Listato 3).

Come generare i DAO


Una volta terminati i metodi dedicati alla scrittura di VO non ci resta che iniziare a lavorare su quelli che scriveranno i DAO. Per motivi di semplicit, in questo articolo ci limiteremo a descrivere il metodo per impostare i DAO con listruzione SQL SELECT * FROM PERSONA. Il lettore pu a sua discrezione aumentare il numero di istruzioni SQL che il generatore dove implementare in tutte le classi. Analogamente al metodo voGenerate(), iniziamo a scrivere il metodo daoGenerate() (Listato 4), che avr il compito di scrivere la struttura base delle classi DAO. Allinterno dello stesso verranno richiamati tanti metodi quante saranno le istruzioni SQL con le quali si vorranno dotare le classi. Nel nostro caso verr richiamato solamente il metodo methodSelectAll() (Listato 4), allinterno del quale si trova il codice per listruzione SQL SELECT * FROM PERSONA. Come probabilmente molti avranno notato, il metodo methodSelectAll() utilizza un altro metodo, forse il pi importante di tutti: methodQueryResult() (Listato 4).

Reflaction
Il methodQueryResult() fornisce a tutte le classi DAO il metodo getQuer yResult(); esso, avvalendosi della Reflection, permette a tali classi di trasformare il java.sql.ResultSet (restituito dalle quer y), in una java.util.Collection di VO associati. Il package della reflection, java.lang.reflect, stato introdotto con la release 1.1 dellSDK Java della SUN per permettere di manipolare gli oggetti a runtime senza che sia necessario conoscerne la struttura durante la fase di compilazione. Ovviamente avremmo potuto decidere di realizzare il nostro generatore senza fare ricorso a questa API (ma il codice sarebbe stato pi complesso).

Conclusioni
Esistono aziende che puntano il loro core business sul solo sviluppo di generatori di codice. Dico ci per far capire come questo tipo di strumento possa arrivare a gradi di complessit tali da rendere questo articolo una semplice e veloce introduzione allargomento. Mi auguro comunque di aver destato sufficiente interesse verso questo tipo di software che oltre ad essere molto utile durante la realizzazione di un prodotto, anche molto divertente da realizzare.

sar di impacchettare queste informazioni allinterno di un oggetto java.util.Hashtable (Listato 3).

Come generare i VO
Adesso che abbiamo tutte le informazioni che ci servono, possiamo iniziare a lavorare sulle classi VO. Per farlo ci avvarremmo del metodo voGenerate() (Listato 3). Una analisi veloce di questo metodo permette di distinguere due blocchi di codice distinti: il primo, caratterizzato da un ciclo while, dedicato alla scrittura delle propriet private della classe; il secondo, anchesso caratterizzato da un ciclo while, dedicato alla scrittura dei metodi get() e set() necessari a gestire i valori che verranno memorizzati allinterno delle
>> 82

Riferimenti
[1] Java java.sun.com

Carlo Camusso
Si occupa di soluzioni enterprise per la Pubblica Amministrazione e le Unit Sanitarie Locali.
DEV > n. 117 aprile 2004

Potrebbero piacerti anche