MokaByte
Numero 11 - Settembre 1997
|
|||
|
III parte |
||
Giovanni Puliti |
Finalmente molti esempi per imparare a fare transazioni | ||
L'impostazione
che ho dato all'articolo è prettamente schematica, e i vari argomenti
sono stai affrontati in sequenza; non è stato riportato un esempio
completo per dare maggior spazio all'esposizione di piccoli porzioni di
codice con i quali analizzare tutte le varie casistiche di accesso e di
gestione di basi di dati.
Si presume una
certa familiarità del lettore con i concetti di base legati alla
gestione di database e con le librerie di base per gestire tali oggetti
(vedi JDBC).
Tutta la trattazione
che segue è del tutto generica ed adattabile ad una qualsiasi tipo
di DB e DBMS, anche se in questa sede abbiamo utilizzato un database di
tipo mdb (in particolare gli esempi fanno riferimento all'ormai stranoto
biblio.mdb) gestito da ODBC. Per maggior semplicità e per permettere
a tutti di mettere in pratica gli esempi proposti senza dover acquistare
driver particolari, si è utilizzato il bridge JDBC-ODBC, direttamente
disponibile in JDK.
Rispetto agli
articoli precedenti in un certo senso il presente è una ripetizione
più superficiale degli argomenti già trattati.
Senza riprendere
in mano tutta la teoria di JDBC API ricordo brevemente che esse non permettono
la gestione diretta di un database, ma solo l'invio di istruzioni in un
linguaggio standard (SQL), e la manipolazione degli eventuali risultati.
Affronteremo quindi tutti gli oggetto necessari per instaurare questo coloquio
bidirezionale.
Molto sinteticamente
i passi necessari da compiere sono i seguenti:
// Caricamento del driver per mezzo del settaggio// della proprietà di sistema sql.drivers.
Properties prop = System.getProperties();
prop.put("jdbc.drivers","sun.jdbc.odbc.JdbcOdbcDriver");
System.setProperties(prop);
//caricamento del driver per dichiazione esplicita
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
//controllo del caricamento
try{
Connection connessione=DriverManager.getConnection("jdbc:odbc:biblio");
Enumeration enum = DriverManager.getDrivers();
System.out.println("Driver ="+enum.nextElement());
while (enum.hasMoreElements())
System.out.println("Driver"+enum.nextElement());
}
//sezione di gestione delle eccezioni
catch(SQLException sqle){
System.out.println("ERRORE SQL "+sqle.toString());
SQLException s=sqle.getNextException();
while (s!=null){
System.out.println(s.toString());
s=sqle.getNextException();
}
try{
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
Connection con=DriverManager.getConnection("jdbc:odbc:biblio");
Enumeration drivers = DriverManager.getDrivers();while (drivers.hasMoreElements()){
Driver driver=(Driver)(drivers.nextElement());
System.out.println("Driver :"+driver);
System.out.println("acceptURL :"+driver.acceptsURL("jdbc:odbc:biblio");
System.out.println("Minor Version :"+driver.getMinorVersion());
System.out.println("Major Version :"+driver.getMajorVersion());
System.out.println("JDBC-Compliant :"+driver.jdbcCompliant());
}
}
Per poter eseguire istruzioni SQL le JDBC API mettono a disposizione tre metodi
// utilizzo di execute di una Statement senza ResultSet: *// Creazione di una tabella
try{
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
connessione=DriverManager.getConnection("jdbc:odbc:biblio");
Statement st= connessione.createStatement();
sql="CREATE TABLE tabella ( nome CHAR(100), anni Integer )";
risult=st.execute(sql);
numris=st.getUpdateCount();
}
Ecco due
esempi che mostrano l'uso della execute() in abbinamento con la
getUpdateCount() e con e senza resituzione di recordset (ResultSet).
// Esempio di execute() di una Statement senza ResultSet: SELECT SQLIl metodo executeQuery() è molto simile al precedente ma più semplice e limitato (ad esempio non può inviare istruzion SQL che prevedano risultati multipli). Esso permette semplicemente l'esecuzione di una istruzione SQL rappresentata dal suo argomento di tipo stringa. Restituisce un valore singolo di tipo ResultSet. L'uso di tale metodo sia del tutto equivalente al metodo execute().try{
DriverManager.setLogStream(null);
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
connessione=DriverManager.getConnection("jdbc:odbc:biblio");
connessione.setAutoCommit(false);
Statement st= connessione.createStatement();
sql="SELECT name FROM Authors";
risult = st.execute(sql);
numris=st.getUpdateCount();
connessione.rollback();
// execute() di una Statement con ResultSet: UPDATE SQL
sql = "UPDATE Authors SET Name = \'Pippo\' WHERE Name=\'Topolino\'";
ResultSet rs = st.getResultSet()
risult = st.execute(sql);
numris = st.getUpdateCount();
}
// Esempio di executeUpdate di una Statement.try{
DriverManager.setLogStream(null);
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
connessione=DriverManager.getConnection("jdbc:odbc:biblio");
connessione.setAutoCommit(false);
Statement st= connessione.createStatement();
String sql="UPDATE Authors SET Name = \'Topolino\' WHERE age < 30";
numris = st.executeUpdate(sql);
numris2=st.getUpdateCount();
risult = st.getResultSet();
connessione.rollback();
}
Esecuzione
di istruzioni precompilate.
La volta scorsa
abbiamo visto che è possibile ottimizzare le operazioni di colloquio
con il database per mezzo di istruzioni SQL precompilate.
Questa operazione
può essere effettuata per mezzo della classe PreparedStatement,
che permette di velocizzare di molto le transazioni utilizzando stringhe
già precompilate e variando di volta in volta solo alcuni parametri:
questo permette di eseguire tutte le operazioni interne al DBMS per la
gestione della transazione una sola volta, e di riutilizzare, con poche
modifiche, il lavoro già fatto per le esecuzioni successive.
Anche in questo
caso per eseguire istruzioni SQL si possono utilizzare i tre metodi execute(),
executeQuery() e executeUpdate(), che però in questo
caso devono essere invocati senza parametri (è l'oggetto statement
che ingloba la stringa SQL precompilata).
Un oggetto PreparedStatement
viene creato a partire da un oggetto Connection per mezzo del metodo prepareStatement(),
al quale si deve passare in input una stringa rappresentante lo statemente
SQL precompilato.
Per creare un
PreparedStatement si deve prima costruire l'istruzione SQL, lasciando dei
caratteri jolly "?" (placeholder ) dove si dovrà di volta in volta
introdurre un parametro diverso.
Per settare
tali placeholder utilizzano metodi del tipo setXXX() dove XXX indica il
tipo del valore che deve essere inserito.
Il compito di
questi metodi è quello di convertire automaticamente il tipo Java
in tipo SQL: ad esempio setString() coverte il tipo del valore passato
da String a CHAR, VARCHAR o LONGVARCHAR a seconda della lunghezza della
variabile stessa.
I metodi di
questo tipo ricevono due parametri: il primo indica la posizione del placeholder,
mentre il secondo contiene il valore.
Se non si conosce
il tipo del valore da immettere si può alternativamente utilizzare
il metodo setObject(), ed in questo caso il driver si occupa del
riconoscimento del tipo e della relativa conversione in tipo SQL.
Ecco un esempio
analogo ai precedenti ma in cui è prevista la creazione ed il completamento
di un PreparedStement.
// Esempio di preparazione di una PreparedStatement e di successiva// istanziazione
try{
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
connessione=DriverManager.getConnection("jdbc:odbc:catalogocd");
connessione.setAutoCommit(false);
String sql= "UPDATE SET Name = \'Pippo\' WHERE age < ?";
PreparedStatement pst= connessione.prepareStatement(sql);
pst.setInt(1,30);
pst.execute();
connessione.rollback();
}
E' interessante
a tal punto osservare le prestazioni di esecuzione di statement SQL normali
a confronto con preparedstatement precompilati. Le fasi da eseguire sono
// Esempio di preparazione di una PreparedStatement.// Confronto delle prestazioni
try{
DriverManager.setLogStream(null);
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
connessione=DriverManager.getConnection("jdbc:odbc:biblio");
connessione.setAutoCommit(false);
String sql="UPDATE Authors SET Name = \'Pippo\' WHERE age < ?";
Statement st= connessione.createStatement();
inizio_st = (new java.util.Date()).getTime();
for(int i=0; I<times; i++)
st.execute(sql);
fine_st = (new java.util.Date()).getTime();
System.out.println("Tempo per "+ times+" execute "+fine_st-inizio_st);
PreparedStatement pst= connessione.prepareStatement(sql)
pst.setInt(1,40);
inizio_pst = (new java.util.Date()).getTime();
for(int i=0; I<times; i++)
pst.execute();
fine_pst = (new java.util.Date()).getTime();
System.out.println("Tempo per "+ times+" execute "+fine_pst-inizio_pst);
connessione.rollback();
}
Esecuzione
di QUERY
L'interfaccia
CallableStatement è una ulteriore estensione delle altre viste in
precedenza, e permette di richiamare istruzioni definite nel DBMS. Il nome
con cui queste funzioni (stored procedure) dipende dal particolare gestore
di DB: ad esempio in MS Access (o più precisamente in ODBC per mdb)
si chiamano Query. Le considerazioni relative ai metodi di manipolazione
del db sono analoghe al caso precedente, tranne per la modalità
di creazione dell'oggetto CallableStatement a partire da un Connection
e per il fatto che adesso non solo è possibile manipolare parametri
in entrata (parametri IN), ma anche in uscita (parametri OUT).
Per utilizzare un CallableStatement si devono seguire i seguenti passi, come fatto nell'esempio seguente:
// Creazione di una CallableStatement *ELABORAZIONE DI RISULTATItry{
DriverManager.setLogStream(null);
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
connessione=DriverManager.getConnection("jdbc:odbc:biblio");
connessione.setAutoCommit(false);
String sql= "{ ? = call provacall ? }";
CallableStatement pst= connessione.prepareCall(sql);
pst.setInt(2,1990);
pst.registerOutParameter(1,Types.NUMERIC);
pst.execute();
ris=pst.getInt(1);
connessione.rollback();
}
try{Reperire Maggiori InformazioniDriverManager.setLogStream(null);
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
connessione=DriverManager.getConnection("jdbc:odbc:biblio");
connessione.setAutoCommit(false);
String sql= "Select AuID,Name FROM Authors WHERE age<30";
Statement st= connessione.createStatement();
ResultSet ris = st.executeQuery(sql);
while (ris.next()){
ResultSetMetaData rsmd = ris.getMetaData();
int num_col=rsmd.getColumnCount();
System.out.println("Numero colonne "+num_col);
for (int i=0; i<num_col; i++){
System.out.println("Nome Colonna "+i+" = "+rsmd.getColumnName(i+1));
System.out.println("Label Colonna "+i+" = "+rsmd.getColumnLabel(i+1));
}
System.out.print("AuID ="+ris.getString(1));
System.out.println("Name ="+ris.getString(2));
}
connessione.rollback();
}
// Ritorna SI se vero e NO se falsoTRANSAZIONIprivate String siono(boolean val){
return (val)?"SI":"NO";
}
try{
DriverManager.setLogStream(null);
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
connessione=DriverManager.getConnection("jdbc:odbc:biblio");
DatabaseMetaData dati= connessione.getMetaData();
System.out.println("--------------------");
System.out.println(" DATABASEMETADATA");
System.out.println("--------------------");
System.out.println("allProceduresAreCallable? "+siono(dati.allProceduresAreCallable()));
System.out.println("allTablesAreSelectable? "+siono(dati.allTablesAreSelectable()));
System.out.println("URL "+dati.getURL() );
System.out.println("UserName "+dati.getUserName());
System.out.println("isReadOnly? "+siono(dati.isReadOnly()));
}
GESTIONE DELLE
ECCEZIONI
Ogni volta che
si esegue una istruzione relativa ad una connessione con un DBMS, si può
incorrere nella generazione di una eccezione o di un warning, a causa di
una qualche irregolarità.
Le JDBC API
mettono a disposizione una particolare classe detta SQLException che offre
tutte le informazioni specifiche dell'inconveniente verificatosi. Ad esempio
è possibile conoscere:
Conclusioni
Termina qui
questo articolo pratico su JDBC e gestione database con Java, e termina
anche la miniserie iniziata tre mesi addietro. Dal colloquio con alcuni
lettori via mail e direttamente dal vivo in occasione del Borland Forum
tenutosi a Milano in Giugno, noi della redazione abbimao avuto modo di
constatare l'alto interesse che l'argomento suscita per cui spero di esser
riuscito ad affrontarlo in maniera sufficientemente approfodita.
Un ringraziamento
particolare va a Massimo Carli che si è preoccupato della stesura
degli esempi e senza il quale non sarei riuscito a realizzare l'articolo
in tempo utile per la pubblicazione.
|
||
|
||
MokaByte ricerca
nuovi collaboratori
|
||
|