Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Carlo Camusso Si occupa di soluzioni enterprise per la Pubblica Amministrazione e le ` Unita Sanitarie Locali .
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
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
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.
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 }
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).
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.
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).
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.
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