Database use with Java and Jdbc |
Home Page | Comments | Articles | Faq | Documents | Search | Archive | Tales from the Machine Room | Contribute | Set language to:en it | Login/Register
Java consente lo sviluppo di applicazioni portabili da un sistema all'altro (quasi) senza modifiche ne' ricompilazione.
Questa e' senz'altro una bella cosa, finche' non ci troviamo con una applicazione che necessita di memorizzare e/o accedere a delle informazioni strutturate e conservate in un database.
A questo punto abbiamo svariate possibilita': possiamo gestirci tutto il lavoro internamente (scrivendo il codice necessario), oppure possiamo utilizzare le classi specifiche per l'accesso ai database. Queste "classi" costituiscono JDBC: lo standard di accesso ai database per Java.
Nel codice di esempio presentato, viene sviluppata una semplicissima applicazione che consente l'accesso ad un database qualsiasi, l'invio ed esecuzione di interrogazioni ed il recupero di dati.
Per cominciare pero', un po' di teoria.
Esistono svariati tipi di database che possono essere utilizzati, ognuno ha i suoi punti di forza e di debolezza, Java consente di usarli piu' o meno tutti, occorre pero' conoscere le varie caratteristiche per scegliere il database che piu' si adatta alle proprie esigenze.
I cosiddetti "Flat File" memorizzano le informazioni in modo non strutturato in un unico file (di solito leggibile), ma forniscono ben poche funzionalita' a parte la memorizzazione ed il recupero delle stesse informazioni.
In genere l'accesso ai dati e' estremamente veloce, ma occorre ritrovare le informazioni utilizzando una ricerca di tipo "preciso" (non si puo' dire "cercami tutto quello che e' simile a..."), inoltre raramente supportano la multiutenza (piu' utenti contemporaneamente collegati).
Solitamente, i database sono utilizzati per memorizzare informazioni in modo "strutturato", utilizzando tabelle, records (singole righe di una tabella) e campi (singoli elementi all'interno di un record). Questo tipo di struttura consente un facile accesso/reperimento delle informazioni, migliorandone la gestione, inoltre permette la costruzione di indici che accellerano le ricerche.
Un Database Relazionale (RDBMS) permette la composizione di interrogazioni o query che collegano piu' tabelle, stabilendo delle "relazioni" tra i contenuti delle singole tabelle. Solitamente l'interrogazione del database viene fatta usando uno specifico linguaggio denominato SQL, a seconda delle capacita' (e del costo spesso) del database, possono essere presenti ulteriori capacita', quali la possibilita' di impostare controlli automatici di coerenza tra i dati (il dato nel campo X della tabella Y deve essere presente in uno dei record della tabella Z), l'esecuzione di procedure interne al database stesso quando richiamate esplicitamente (Stored Procedure) o in automatico quando si verificano cambiamenti ad un record in una tabella (Trigger).
Un database Object Oriented (OODBMS), gestisce le informazioni considerandole come "oggetti" con varie "proprieta'", invece che records con campi. Il risultato finale e' sempre lo stesso, ma viene svolto con differente orientamento.
Un database Object-Relational (ORDBMS), combina la gestione di un RDBMS con l'orientamento agli oggetti di un OODBMS, pertanto ha le forze e le debolezze di entrambi.
Per "livelli" (tier) si intende quelli che passano tra la propria applicazione ed i dati che sono gestiti.i
In una applicazione "single-tier", l'applicazione stessa gestisce i suoi dati direttamente. Questo tipo di applicazioni sono (in genere) altamente specializzate e richiedono un'accesso ai dati ultra-veloce. Fanno solitamente uso di database flat-file, che vengono letti/scritti usando delle librerie specializzate compilate nell'applicazione stessa.
Una applicazione "two-tier" indica che il programma stesso "parla" con un server di database che si preoccupa di gestire i dati e ritornare i risultati. Questo tipo di applicazioni e' il piu' comune. La "comunicazione" con il database puo' essere fatta tramite librerie particolari o tramite un driver caricato a run-time dall'applicazione stessa. Questa seconda possibilita' in genere implica che il database puo' essere cambiato senza troppe modifiche al programma stesso.
Una applicazione "three-tier" (o di piu') implica che tra la stessa ed il server di database ci sono uno o piu' "application server" che aumentano il livello di isolamento. L'applicazione cioe', invia le proprie richieste ad un'altra applicazione server, la quale parla con un'altra applicazione, la quale parla con un'altra applicazione... alla fine qualcuno parla con il database.
In generale piu' livelli ci sono tra l'applicazione ed il database e meno questi due elementi sono interdipendenti. Il che e' un bene dal punto di vista della possibilita' di cambiare database (o struttura dello stesso) senza cambiare l'applicazione, ma e' un male dal punto di vista della velocita' dell'applicazione nel reperire le informazioni.
Teniamo sempre conto poi che una parte dipendente dal database ci deve essere da qualche parte.
Una architettura "multi-tier" permette anche di distribuire il carico di lavoro tra varie macchine, migliorando quindi l'utilizzo di tutta la rete, ma e' anche dipendente da tutti i problemi che possono risultarne (dal cracking fino alla saturazione della rete stessa).
Anche nota come JDBC, e' un "livello di astrazione" definito da JavaSoft che fornisce ad una applicazione (java ovviamente) un'SQL standard ed un'insieme di classi per l'accesso e l'utilizzo di database.
Che significa tutto cio'? Molto semplice. Quando da un'applicazione Java vogliamo usare un database, invece di smazzarci il collegamento diretto, invochiamo la creazione di una classe driver, che ci fornira' una serie di classi standard, tramite i metodi di queste classi (metodi che sono specificati dalle interfaccie indicate nello standard JDBC), siamo in grado di accedere ai nostri dati, leggerli, modificarli etc. etc. Il tutto senza preoccuparci piu' di tanto del tipo di database o dello specifico dialetto SQL utilizzato. Il nostro driver si occupera' di tradurre il tutto e fornirci i risultati.
JavaSoft ha definito quali interfaccie devono essere implementate dai vari driver, e quali metodi sono inclusi in ogni interfaccia, sta' poi ai vari "produttori" fornire un driver che segua le specifiche.
A seconda del funzionamento, i driver si dividono in vari tipi:
Driver di tipo 1 (type 1 driver)
Questo tipo di driver traduce i metodi di chiamata JDBC in chiamate dirette verso un'altro driver fornito da un'altro produttore, cio' significa che entrambi i driver devono essere presenti perche' l'applicazione possa funzionare. Inoltre la presenza di livelli multipli tra i due driver puo' peggiorare le prestazioni del sistema. Un tipico esempio di driver di questo tipo e' il bridge Jdbc/Odbc.
Driver di tipo 2 (type 2 driver)
Questo tipo di driver traduce le chiamate JDBC in chiamate alle API native del database stesso. Le prestazioni sono solitamente migliori di un tipo 1. Possono esserci pero' dei problemi di distribuzione in quanto il driver puo' richiedere la presenza di librerie del database stesso (che non sempre possono essere distribuite senza un'apposita licenza) oppure l'installazione sul client di software specifico del database (client software) che potrebbe essere piuttosto costoso.
Driver di tipo 3 (type 3 driver)
Questo tipo di driver e' il piu' comune da trovare, il driver traduce le chiamate JDBC in chiamate ad un database-server usando le API di comunicazione sulla rete (socket). L'unico problema e' che e' richiesta la presenza di un database server o di una applicazione che ne emuli il funzionamento. La distribuzione di questo tipo di driver non presenta (di solito) problemi, la configurazione purtroppo richiede la conoscenza dell'indirizzo IP del server o del suo nome (se e' presente un DNS nella rete) e della porta che il server usa per ricevere chiamate.
Driver di tipo 4 (type 4 driver)
Questo tipo di driver traduce le chiamate JDBC in chiamate dirette al server usando le API della rete. Il driver e' (in sostanza) specifico per il singolo database. Questo tipo di driver presenta gli stessi problemi del tipo 3 sopramenzionato.
Non tutti i driver rientrano in uno di questi tipi, alcuni driver sono (per esempio) 100% java ed accedono direttamente un database locale. Questo tipo di driver sono ottimi per applicazioni desktop, ma non rientrano esattamente in nessuno dei tipi sopra esposti. Alcuni produttori forniscono JDBC driver che prevedono determinati vantaggi in varie applicazioni.
Il tipo di database scelto per la propria applicazione puo' impattare sulla difficolta' di codifica e sulla complessita' dell'applicazione stessa. Sicuramente pero' impatta sulla velocita' ed abilita' nel reperire le informazioni. Inoltre puo' essere utile spostare determinate funzioni all'interno del database stesso (in Stored Procedre o in Trigger).
Per utilizzare completamente un design Object-Oriented, in molti casi e' preferibile l'uso di un database Object-Oriented.
Altri fattori da tenere in considerazione nella scelta del database sono la robustezza dello stesso, il formato nel quale sono memorizzati i dati, l'effettivo supporto per piu' utenti contemporanei (multiutenza), la disponibilita' su piu' piattaforme (portabilita') ed ovviamente il costo dello stesso.Robustezza
Con "robustezza" si intende, non solo l'attitudine del database a bloccarsi e/o danneggiarsi autonomamente, ma anche la possibilita' o facilita' nel recuperare le informazioni nel malaugurato caso di crash della macchina o danneggiamento della struttura dati.
Il supporto (ad esempio) di backup "a caldo" (senza dover arrestare il database stesso), aumenta di molto le possibilita' che i dati possano essere recuperati senza grossi problemi anche in caso di crash del sistema.
Formato del database
Un database puo' essere standard o no, un db che non e' standard utilizzera' propri tools per accedere alle informazioni e consentirne la modifica.
Tutti (o quasi) i database server utilizzano un formato di memorizzazione dei dati che e' proprietario, per accedere ai dati in genere questi database richiedono una login, cioe' l'utente deve fornire una password, inoltre la visibilita' di certe informazioni e' modificabile da parte di un amministratore che puo' scegliere se mostrare o no certe informazioni.
Certi database flat-file utilizzano una struttura proprietaria, il che implica che per accedere al database stesso e' richiesto uno specifico driver e certe informazioni (struttura delle tabelle), altri flat-file usano invece una struttura standard che consente il reperimento delle informazioni usando tools standard (dBase III per esempio).
Benche' quest'ultimo formato sia meno "sicuro", e' certamente preferibile nel malaugurato caso di crash dell'applicazione, perche' consente sempre l'accesso ai dati.
Multi-user o Single-user
Occorre fare molta attenzione alla possibilita' di utenti multipli che utilizzano lo stesso database nello stesso momento. Il blocco di record e tabelle e' in genere, supportato da tutti i database, ma ognuno con metodi e finalita' diverse.
L'applicazione deve comunque essere scritta in modo da trarre vantaggio di questi meccanismi (transazioni, lock etc.).
Portabilita'
Dato che e' uno dei punti di forza di Java, e' obbligatorio considerare la portabilita' del proprio sistema. In particolare la presenza di driver per vari sistemi (Windows/Linux/Unix/Mac), ed eventualmente l'esistenza di database multipiattaforma.
Costo
Che dire riguardo a questo ?
JDBC e' composto da varie classi che "cooperano" per fornire l'accesso ai dati del database.
In prima linea viene il Driver, che interpreta tutte le funzioni richiamate e le 'traduce' per il database, il Driver deve essere caricato in memoria, una volta fatto questo e' possibile utilizzarne le funzioni per creare una Connection, che incapsula il vero e proprio collegamento al database. Una Connection e' necessaria per poter fare qualunque altra cosa.
Una volta ottenuta la Connection siamo in grado di usare quest'ultima per produrre Statement, che verranno a loro volta usati per ottenere ResultSet, i quali (puff puff) contengono i dati.
Non e' sempre necessario l'uso di un ResultSet. Se vogliamo semplicemente eseguire delle modifiche sui dati (usando una UPDATE, INSERT o DELETE), possiamo usare il metodo executeUpdate() direttamente sullo Statement.
Il driver viene caricato in memoria in due modi: o usando un caricamento esplicito:
Class.forName("nome.della.classe.del.driver");
oppure usando DriverManager per eseguire il caricamento:
DriverManager.registerDriver(new nome.della.classe.del.driver());
In entrambi i casi il nome della classe del driver e' necessario, tale nome e' (di solito) riportato nella documentazione che accompagna il driver stesso.
Una volta che il driver e' in memoria possiamo usare DriverManager per ottenere una connessione al database, per fare cio' ci serve un URL al database.
Questo e' il "percorso" per accedre al database. Un URL e' composto in genere da vari parametri separati da ":" o ";". Uno di questi parametri e' l'indirizzo IP o il nome del server in questione. Altri parametri sono tipici del database stesso e non possono essere previsti. Anche per questo, tutti i parametri sono descritti nella documentazione che accompagna il driver.
Un esempio di URL potrebbe essere il seguente:
jdbc:inetdae:172.20.118.106:1433
In alcuni casi il database richiede anche un'autenticazione, cioe' un nome utente ed una password che siano validi. In genere questi sono parametri aggiuntivi da passare nell'URL del database stesso.
Una volta che abbiamo il nostro URL possiamo usare la DriverManager.getConnection() per ottenere una connessione al database. Se qualche cosa e' sbagliato (URL o altro), otterremo una bella (si fa per dire) eccezione.
Che ci si fa con la connessione ? Tutto quello (o quasi) che vogliamo.
Una volta ottenuta la connessione possiamo interrogare il database inviandogli una SELECT:
Per prima cosa costruiamo un PreparedStatement:
String sql="SELECT * FROM nomedellatabella WHERE nomecampo=?"; PreparedStatement p=conn.prepareStatement(sql);
Una volta ottenuto il PreparedStatement possiamo inserire il parametro di filtratura usando uno dei metodi setXxxxxx(), a seconda che il nostro parametro di filtro sia un numero o una stringa. L'utilita' del PreparedStatement rispetto al costruire una stringa SQL "a mano" e' che gestisce lui le conversioni di ' in '' e dei vari formati numerici. Inoltre aggiunge i vari apici se necessario.
p.setString("questo e' il filtro");
Una volta impostati tutti i parametri, possiamo eseguire la query ed ottenre un ResultSet in risposta:
ResultSet r=p.execute();
Il ResultSet potrebbe essere vuoto o contenere 'n' record, per controllarne il contenuto si verifica se il metodo next() ritorna true o false:
while(r.next()) { // eseguo il controllo dei dati }
Attenzione: per motivi di portabilita' tra i vari driver, i campi da un ResultSet possono essere letti solo nell'ordine in cui sono dichiarati nella SELECT ed una sola volta. Esistono driver che consentono di aggirare tali limitazioni, ma per evitare problemi e' sempre meglio seguire le specifiche.
L'accesso ai singoli campi del ResultSet puo' essere fatto sia usando il nome del campo, sia usando un numero progressivo:
rs.getXXXX(1); // ritorna il primo campo rs.getXXXX("nomecampo"); // ritorna il campo "nomecampo"
Le funzioni getXxxxx() convertono automaticamente il tipo di campo nel tipo richiesto.
Una volta concluso l'utilizzo del ResultSet, possiamo chiuderlo per distruggere il corrispondente cursore e liberare risorse, ricordiamoci che il ResultSet e' connesso ad uno Statement, quindi chiudere lo Statement equivale a chiudere anche il cursore. Ma fare le cose pulite e' sempre meglio, quindi:
r.close(); // chiude il ResultSet s.close(); // chiude lo Statement
Il metodo getMetaData() ritorna un'oggetto di tipo MetaData che contiene informazioni relative al tipo ed al numero di campi contenuti nel ResultSet. Analogamente possiamo ottenere informazioni sul contenuto dell'intero database tramite la Connection.
L'accesso a questo tipo di informazioni e' utile se stiamo scrivendo una funzione di accesso generico a database o se vogliamo controllare l'esistenza di una certa tabella prima di eseguire una qualche query.
Il codice di esempio e' una semplice interfaccia generica per l'accesso ad un qualunque database di cui sia fornito il driver JDBC.
Non e' niente di complicato ne' sofisticato, quindi non aspettiamoci niente di straordinario.
Qualche spiegazione sul codice:
1. configurazione
Dato che il programma e' generico, il driver e l'URL sono definiti
esternamente, nel corrispondente .conf file. In questo file i
parametri sono definiti come
2. classpath
Il programma si aspetta di trovare il driver jdbc nel classpath, quindi
o mettiamo il .jar nella $JAVA_HOME/jre/lib/ext, o passiamo il
classpath sulla riga di comando di java durante l'esecuzione con
java -classpath .:path/per/driver.jar DBI (nota, su Windows
occorre usare ';' invece di ':' per separare i path).
3. funzionamento
Quando richiamata, la classe tenta di aprire una connessione al
database usando i parametri specificati nel file di configurazione, se
riesce nell'intento presenta un prompt sql> ed aspetta che
l'utente digiti una query. La stessa viene passata al database ed i
risultati vengono mostrati di seguito.
Il programma fa' una minima elaborazione dei dati per presentare i nomi dei campi e gli stessi con un minimo di struttura, ma non piu' di tanto.
Contenuto dell'archivio:
dbi.jar e' il codice compilato ed "impacchettato" in un'unico archivio compresso.Esecuzione del programma
Per eseguire il programma richiamare la classe di avvio DBI specificando il classpath per il driver JDBC da usare.
Comments are added when and more important if I have the time to review them and after removing Spam, Crap, Phishing and the like. So don't hold your breath. And if your comment doesn't appear, is probably becuase it wasn't worth it.
stecolna By stecolna posted 24/09/2008 09:26
Davide, non è che potresti postare un piccolo articolo sull'uso dei RecordSet JDBC disconnessi? Se chiedo troppo ti chiedo scusa. Ciao
me lo segno
stecolna By stecolna posted 24/09/2008 12:39
Davide Bianchi, works as Unix/Linux administrator for an hosting provider in The Netherlands.
Do you want to contribute?
read how.
This site is made by me with blood, sweat and gunpowder, if you want to republish or redistribute any part of it, please drop me (or the author of the article if is not me) a mail.
This site was composed with VIM, now is composed with VIM and the (in)famous CMS FdT.
This site isn't optimized for vision with any specific browser, nor
it requires special fonts or resolution.
You're free to see it as you wish.