Connessione ai database
La connessione di Java ai database avviene tramite atti formali che poi
si riscontrano anche in altri linguaggi.
Per comodità, noi realizziamo la connessione con dei database Access (2003),
ma per altre versioni di Access o per collegamenti a DB SQL cambia solo
la stringa di connessione.
Lavorando con Access, facciamo riferimento ad un file su disco che ha
un nome ed una estensione (mdb).
Un altro identificatore importante è il nome della tabella o delle tabelle
sulle quali eseguiamo le operazioni.
Infine, dobbiamo aver sempre presente l'esatto nome dei campi (colonne)
del database che vogliamo interpellare.
Creiamo un file (database) Access chiamato 'data.mdb' al suo interno scriviamo
la tabella 'compensi' :
i
|
nome
|
target
|
1
|
laura
|
7
|
2
|
claudio
|
5
|
3
|
marco
|
4
|
4
|
paola
|
3
|
il campo 'i' è chiave primaria (non ammette duplicati).
Possiamo interrogare il database via Java attraverso uno scritto come
questo:
import java.sql.*;
public class connessione {
public static void main(String[] args) throws SQLException {
Connection cn;
Statement st;
ResultSet rs;
String url,sql;
//________________________________connessione
try {
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
}catch (
ClassNotFoundException e) {
System.out.println("ClassNotFoundException: "); System.err.println(e.getMessage());
}//fine try-catch
url = "jdbc:odbc:;DRIVER={Microsoft Access Driver (*.mdb)};DBQ=data.mdb";
cn = DriverManager.getConnection(url,"","");
sql="SELECT * FROM compensi;";
//________________________________query
try{
st=cn.createStatement();
rs=st.executeQuery(sql);
while(rs.next()==true) System.out.println(rs.getString("nome")+"\t"+rs.getString("target"));
}catch(SQLException e) {
System.out.println("errore:"+e.getMessage());
}//fine try-catch
cn.close(); //chiusura connessione
}//fine main
}//fine classe
Per collegarci al database ci serve un oggetto Connection (cn)
uno Statement (st) che si occupa di effettuare
l'interrogazione (query) un ResultSet (rs).
Il ResultSet in altri linguaggi (VB, ASP) viene
anche chiamato recordset: essa è in
pratica una tabella che viene prodotta alla fine della query (è il risultato
della query) e risiede nella RAM del computer. Le due istruzioni
st=cn.createStatement();
rs=st.executeQuery(sql);
eseguono materialmente l'interrogazione. In output viene stampata la tabella
senza l'indice i. (infatti, noi gli diciamo di
stampare solo il nome e il target) Questo avviene col ciclo
while(rs.next()==true) System.out.println(rs.getString("nome")+"\t"+rs.getString("target"));
Fondamentali sono la stringa di connessione (url)
url = "jdbc:odbc:;DRIVER={Microsoft Access Driver (*.mdb)};DBQ=data.mdb";
Che dichiara il database con cui ci si collega. Altrettanto importante
è la stringa della query (sql) scritta appunto in linguaggio SQL.
sql="SELECT * FROM compensi;";
Il questo caso l'istruzione dice di selezionare tutti (*) i record (righe)
della tabella compensi.
La query può subire articolazioni, ad esempio:
sql="SELECT * FROM compensi WHERE target>=5;";
avrebbe stampato solo i primi due record; infatti, solo loro soddisfano
la condizione di avere un campo target maggiore o uguale a 5.
SQL
L'istruzione SELECT viene usata per effettuare
delle interrogazioni nel DB, quindi viene usata per operazioni di ricerca
di dati. La sua sintassi è:
SELECT ElencoCampi FROM NomeTabella WHERE condizione;
L'elenco dei campi non è altro che la sequenza delle colonne che devono
essere visualizzate, la clausola WHERE che è comunque
opzionale contiene la condizione logica che deve essere rispettata per
effettuare la selezione.
Le altre operazioni che si possono fare su un DB sono quelle che ci possiamo
immaginare:
operazione
|
istruzione
|
Inserimento
|
INSERT
|
Cancellazione
|
DELETE
|
Aggiornamento
|
UPDATE
|
Bastano? Si direbbe di si, ma ovviamente esistono altre operazioni come
quella corrispondente all'istruzione CREATE che
permette di creare intere tabelle con campi stabiliti da noi. La sintassi
generale per il comando INSERT è:
INSERT INTO NomeTabella (elencocampi) VALUES (elencovalori);
se volessimo applicarla alla nostra tabella, potrebbe essere :
INSERT INTO compensi (nome,target) VALUES ('fabio',5);
Con SQL quando ci riferiamo alle stringhe ('fabio') bisogna usare gli
apici mentre quando ci riferiamo a valori numerici (5) non bisogna usarli.
Il costrutto try-catch si presenta nel modo seguente:
try{
st=cn.createStatement();
st.executeUpdate(sql);
}catch(SQLException e) {
System.out.println("errore:"+e.getMessage());
}
Da notare come deve essere
applicato il metodo executeUpdate(sql); perché si tratta di un aggiornamento
della tabella e non di una query.
Un modo analogo può essere usato per le cancellazioni che in SQL si dichiarano:
DELETE FROM NomeTabella WHERE nomecampo=valore;
se volessimo applicarla alla nostra tabella, potrebbe essere
DELETE FROM compensi WHERE target=5;
Il blocco try-catch rimane invariato ma la nostra tabella viene ridotta
nel modo seguente:
i
|
nome
|
target
|
1
|
laura
|
7
|
3
|
marco
|
4
|
4
|
paola
|
3
|
Questo perchè abbiamo cancellato tutti i record aventi target=5.
Resta da provare l'operazione di aggiornamento.
UPDATE NomeTabella SET nomecampo=valore WHERE nomecampo=valore;
Ad esempio se volessimo portare paola ad un target=5:
UPDATE compensi SET target=5 WHERE nome='paola';
Anche in questo caso il blocco try-catch rimane invariato .
Per la creazione della tabella e la definizione del tracciato dei campi
si usa il comando:
CREATE TABLE NomeTabella (
campo_1 datoCampo_1,
campo_2 datoCampo_2,
. . .
campo_n datoCampo_n);
Per i nostri scopi nei campi della tabella usiamo una chiave primaria
(id) di tipo contatore, di sola lettura che si autoincrementa all'inserimento
di ogni nuovo record (COUNTER PRIMARY KEY).
Per le stringhe di caratteri useremo l'identificatore CHAR(n)
dove n è un intero che definisce la lunghezza massima della stringa. Useremo
dati di tipo intero dichiarati come INTEGER e di
tipo FLOAT per i numeri con la virgola.
Talvolta può essere usato il tipo di dato DATE
per indicare una specifica data del calendario. Più raramente viene usato
il tipo BIT che indica un dato di tipo booleano
(occupa infatti solo un bit di memoria) che può essere zero o non-zero.
A titolo di esempio useremo una tabella di anagrafica per i dipendenti
di una azienda che ha diverse filiali sul territorio:
CREATE TABLE Anagrafica (
id COUNTER PRIMARY KEY,
cognome CHAR(20),
filiale CHAR(20),
funzione CHAR(20),
livello INTEGER,
stipendio INTEGER,
coniugato BIT,
provincia CHAR(2));
Si tratta di un comando piuttosto lungo che può essere gestito con le
seguenti istruzioni:
sql="CREATE TABLE Anagrafica ( ";
sql+="id COUNTER PRIMARY KEY, ";
sql+="cognome CHAR(20), ";
sql+="filiale CHAR(20), ";
sql+="funzione CHAR(20), ";
sql+="livello INTEGER, ";
sql+="stipendio INTEGER, ";
sql+="coniugato BIT, ";
sql+="provincia CHAR(2));";
//________________________________query
try{
st=cn.createStatement();
st.executeUpdate(sql);
}catch(SQLException e) {
System.out.println("errore:"+e.getMessage());
}//fine
try-catch
Il blocco try-catch è invariato rispetto le istruzioni INSERT,DELETE
e UPDATE.
Ora inseriamo i seguenti dati anche manualmente, dato che sono pochi.
id
|
cognome
|
filiale |
funzione |
livello
|
stipendio
|
coniugato
|
provincia
|
1
|
alfa
|
milano |
dirigente |
3
|
2200
|
-1
|
MI
|
3
|
beta
|
milano |
impiegato |
4
|
1600
|
0
|
MI
|
3
|
gamma
|
milano |
impiegato |
3
|
1400
|
-1
|
VA
|
4
|
delta |
saronno |
impiegato |
4
|
1500
|
0
|
CO
|
5
|
lambda |
saronno |
operaio |
5
|
1400
|
-1
|
MI
|
6
|
tau |
saronno |
impiegato |
3
|
1350
|
-1
|
CO
|
7
|
omega |
milano |
impiegato |
3
|
1300
|
-1
|
VA
|
8
|
epsilon |
legnano |
impiegato |
4
|
1550
|
-1
|
VA
|
9
|
theta |
legnano |
operaio |
4
|
1400
|
0
|
MI
|
10
|
iota |
legnano |
operaio |
4
|
1350
|
0
|
MI
|
11
|
kappa |
como |
dirigente |
4
|
2100
|
-1
|
CO
|
12
|
omicron |
como |
impiegato |
3
|
1300
|
0
|
CO
|
13
|
sigma |
como |
impiegato |
2
|
1250
|
-1
|
VA
|
14
|
zeta |
como |
operaio |
3
|
1200
|
0
|
MI
|
Notiamo come per convenzione se il campo BIT non
è nullo Access lo riempie con un -1. Per noi il valore 0 corrisponde ad
una persona non coniugata, mentre un valore -1 ad una persona coniugata.
Ovviamente su una base di dati di questo tipo è possibile svolgere già
un buon numero di interrogazioni:
SELECT cognome, filiale FROM Anagrafica WHERE Funzione='operaio';
ci mostra tutti i campi delle persone che sono residenti in provincia
di Como.
SELECT DISTINCT funzione FROM Anagrafica;
Ci restituisce tutte le funzioni dei dipendenti specificate una sola
volta.
Funzioni di aggregazione
Assieme al SELECT possono essere usate funzioni
che intervengono su insiemi di record, chiamate funzioni di aggregazione.
SELECT COUNT(*) AS TOTALE FROM Anagrafica;
ci dice quante righe ci sono nella tabella personale; è stata aggiunta
la clausola AS associata all'identificatore TOTALE
per avere una intestazione della colonna (a quale colonna facciamo riferimento
durante la stampa a video?). In questo caso il blocco di lettura sarà:
try{
st=cn.createStatement();
rs=st.executeQuery(sql); while(rs.next()==true)System.out.println(rs.getString("TOTALE"));
}catch(SQLException e) {
System.out.println("errore:"+e.getMessage());
}//fine try-catch
Le funzioni di aggregazione possono essere usate anche con la clausola
WHERE.
SELECT COUNT(*) AS NUBILI FROM Anagrafica WHERE coniugato=0;
Ci dice quanti dipendenti non sono sposati.
SELECT livello, COUNT(*) AS TOT FROM Anagrafica GROUP BY livello;
ci restituisce l'elenco dei livelli presenti con a fianco il numero di
occorrenze rilevate per ogni livello:
livello
|
TOTALE
|
2
|
1
|
3
|
6
|
4
|
6
|
5
|
1
|
Più avanti si noterà come le query si possano nidificare fra loro
SELECT COUNT(*) AS TOT FROM (
SELECT livello, COUNT(*) FROM Anagrafica GROUP BY livello
);
Conteggia il numero totale di livelli presenti.
La funzione SUM restituisce la somma di tutti
i valori contenuti in una colonna.
SELECT SUM(stipendio) AS TOT FROM Anagrafica WHERE livello=4;
restituisce la somma degli stipendi che appartengolo al 4°livello.
SELECT AVG(stipendio) AS MEDIA FROM Anagrafica WHERE funzione='impiegato';
restituisce la media dello stipendio di un impiegato
SELECT MAX(stipendio) AS MASSIMO, MIN(stipendio) AS MINIMO
FROM Anagrafica;
restituisce il minimo e il massimo stipendio erogato.
Raggruppamenti e ordinamenti
SELECT cognome, stipendio FROM Anagrafica ORDER BY stipendio
DESC;
ci restituisce l'elenco dei dipendenti ordinati da quello che guadagna
di più a quello che guadagna di meno. Per invertire l'ordine basta usare
la parola chiave ASC al posto di DESC.
L'ordinamento è crescente per default.
Se nel SELECT oltre a dei campi normali è presente
una funzione di aggregazione si usa la clausola GROUP
BY.
SELECT funzione, SUM(stipendio) AS SOMMA FROM Anagrafica GROUP
BY funzione;
ci dà l'elenco delle funzioni con a fianco lo stipendio per tutti i dipendenti
che hanno quella funzione.
funzione
|
SOMMA
|
dirigente
|
4300
|
impiegato
|
11250
|
operaio
|
5350
|
Bisogna osservare come
sia obbligatorio usare la clausola GROUP BY quando nel SELECT è presente
una funzione di aggregazione.
Il seguente comando ci consente di analizzare la quantità degli operai,
raggruppati per livello
SELECT livello, COUNT(livello) AS CONTA
FROM Anagrafica
WHERE funzione='operaio'
GROUP BY livello;
livello
|
CONTA
|
3
|
1
|
4
|
2
|
5
|
1
|
Il comando GROUP BY può essere potenziato dalla
presenza della clausola HAVING tramite la quale
è possibile controllare le condizioni dei raggruppamenti effettuati.
SELECT funzione, AVG(stipendio) AS MEDIA
FROM Anagrafica
GROUP BY funzione
HAVING COUNT(*)>2;
Questa query valuta la media dello stipendio dei dipendenti raggruppati
per funzione a patto che per quella funzione ci siano più di 2 dipendenti.
funzione
|
MEDIA
|
impiegato
|
1406,25
|
operaio
|
1337,5
|
HAVING serve, dunque, per specificare delle condizioni
che devono essere soddisfatte per effettuare i raggruppamenti.
Se il comando SELECT contiene la clausola WHERE,
i valori vengono raggruppati dopo aver operato la selezione sulle righe
che rispettano la condizione imposta dal WHERE.
SELECT filiale, COUNT(filiale) AS CONTA
FROM Anagrafica
WHERE funzione='operaio'
GROUP BY filiale
HAVING COUNT(*)>1;
Valuta il numero di operai presenti in ogni filiale a patto che nella
filiale ci siano almeno due operai.
Per certi versi HAVING somiglia al WHERE, ma mentre
il WHERE impone condizioni sulle righe della tabella, HAVING impone le
condizioni sul risultato prodotto dalle funzioni di aggregazione applicati
a gruppi di righe.
Criteri di selezione
Sono le condizioni di ricerca che vengono usate nelle clausole WHERE
ed HAVING. In esse possono essere usati tutti i
simboli delle operazioni relazionali (=, =>,<=, <, >,)
e delle operazioni logiche AND OR
; l'operatore NOT se presente, deve essere applicato
prima di AND o OR.
Un operatore talvolta usato è BETWEEN che controlla
se un determinato valore è compreso all'interno di un determinato intervallo
(estremi inclusi).
SELECT cognome, stipendio
FROM Anagrafica
WHERE stipendio BETWEEN 1500 AND 1300;
come si intuisce è la stessa cosa che eseguire
SELECT cognome, stipendio
FROM Anagrafica
WHERE stipendio<=1500 AND stipendio>=1300;
L'operatore IN verifica, inoltre, se un dato valore
appartiene ad un insieme specificato.
SELECT cognome, provincia FROM Anagrafica WHERE provincia IN
('CO','VA');
cognome
|
provincia
|
gamma
|
VA
|
delta |
CO |
tau |
CO |
omega |
VA |
epsilon |
VA |
kappa |
CO |
omicron |
CO |
sigma |
VA |
L'operatore IN può anche essere preceduto dall'operatore
NOT.
SELECT cognome, provincia
FROM Anagrafica
WHERE provincia NOT IN ('CO','VA');
Restituisce
cognome
|
provincia
|
alfa
|
MI
|
beta |
MI |
lambda |
MI |
theta |
MI |
iota |
MI |
zeta |
MI |
L'operatore LIKE, viene usato quando si eseguono
delle query sui dati di tipo stringa. Vengono usati due metacaratteri
:'?' (punto di domanda) indica un singolo carattere
in quela posizione della stringa '*' (asterico)
indica una qualsiasi sequenza di caratteri in quella posizione della stringa.
ATTENZIONE! Questa sintassi vale solo per Access, in altri DBMS al posto
di '?' si usa '_' (underscore)
e al posto di '*' si usa '%' (percentuale).
LIKE 'abc*' tutte le stringhe che iniziano per
'abc'
LIKE '*abc' tutte le stringhe che terminano per
'abc'
LIKE '*abc*' tutte le stringhe che contengono la
sequenza 'abc'
LIKE '?abc' tutte le stringhe di quattro caratteri
che terminano per 'abc'
SELECT cognome FROM Anagrafica WHERE cognome LIKE 'om*';
restituisce
In Access, nei campi che non sono chiave primaria, sono ammessi anche
campi vuoti. Per andare incontro a questa eventualità si usa il predicato
IS NULL. Ad esempio:
SELECT cognome FROM Anagrafica WHERE coniugato IS NOT NULL
restituisce l'elenco delle persone il cui stato civile è definito. Nel
nostro caso verrà restituito tutto l'elenco dei cognomi dato che nel campo
'coniugato' non vi sono celle vuote.
|