Corso Java : Lezione 5

JDBC API : panoramica

JDBC API : uso nel codice
JDBC API : che cosa è ?


La JDBC API è una Java API che permette di accedere a qualsiasi
tipo di dato in forma “tabellare”, con particolare attenzione ai dati
registrati in un database relazionale.
La JDBC API è utile per scrivere programmi java che gestiscono
queste tre attività :



Connessione ad una sorgente di dati ( ad esempio un database )
Far processare delle query e degli statements di update al database
Recuperare e processare i risultati ottenuti dal database in risposta a delle
query
Esempio
Connection con = DriverManager.getConnection (
"jdbc:myDriver:wombat","myLogin","myPassword“
);
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM Table1");
while (rs.next()) {
int x = rs.getInt("a");
String s = rs.getString("b");
float f = rs.getFloat("c");
}

Questa porzione di codice :







Istanzia un oggetto DriverManager
Realizza una connessione ad un database
Fà un login
Istanzia un oggetto Statement
Manda uno statement SQL al database
Istanzia un oggetto ResultSet
Analizza la risposta del database
Componenti


JDBC API

La API JDBC™ permette l’accesso ai dati ( relazionali ) utilizzando il linguaggio Java™.
Usando la API JDBC, le applicazioni possono eseguire statements SQL, recuperare i
risultati e propagare le modifiche “indietro” alla sorgente dei dati. La API JDBC può
interagire simultaneamente con sorgenti di dati multiple ed eterogenee.
La API JDBC è parte della piattaforma Java platform( sia Java™ Standard Edition che
Java™ Enterprise Edition). La API JDBC 4.0 è divisa in due packages: java.sql and
javax.sql. Entrambi i packages sono inclusi sia in Java SE che in Java EE.
JDBC DriverManager



JDBC Test Suit


La classe DriverManager definisce gli oggetti che possono connettere una
applcazione Java ad un driver JDBC. DriverManager è tradizionalmente la classe
su cui si appoggia l’architettura JDBC.
I pacchetti standard di estensione javax.naming e javax.sql permettono l’utilizzo
di un oggetto DataSource registrato all’interno di una Java Naming and
Directory Interface™ (JNDI). Si possono usare sia i meccanismi di connessione
diretta che indiretta, ma la connessione attraverso un oggetto DataSource è da
preferirsi ogni volta che sia possibile
La JDBC driver test suite permette di determinare quali driver JDBC la nostra
applicazione andrà ad usare. Questi test non sono esaustivi, tuttavia riescono a
testare molte delle funzioni offerte dalla API JDBC.
JDBC-ODBC Bridge

Il brigde software permetter un accesso di tipo JDBC attraverso i drivers ODBC.
Notare che bisogna caricare i binari del driver ODBC su ogni macchina client
della nostra applicazione : l’uso di questi bridge è consigliabile sono in ambiente
corporate ( dove l’aggiornamento delle macchine non è normalmente un
problema ) o in una applicazione che hanno una architettura “three tier”.
Architettura


La api JDBC supporta sia l’architettura 2-tier che la 3-tier
2-tier
Nell’architettura 2-tier l’applicazione parla direttamente con il datasource :
questo richiede che ci sia un driver JDBC specializzato per il datasource che
si accede. I comandi inviati dall’applicazione vengono instradati dal
datasource così come le risposte. Il driver può trovarsi in un server diverso a
cui il client accede attraverso dei protocolli di rete
Architettura


La api JDBC supporta sia l’architettura 2-tier che la 3-tier
3-tier
Nell’architettura 3-tier i comandi vengono inviati ad un “middle tier server” che
si occupa di instradare i comandi al datasource che restituisce i risultati al
middle tier che li restituisce al client. Questo tipo di architettura permette di
semplificare lo sviluppo delle applicazioni lasciando che la parte di
“sicurezza” e di gestione delle connessioni sia gestita dal middle tier
Stabilire una connessione
Tipicamente, una applicazione che fà uso della JDB API usa



DriverManager : l’applicazione carica direttamente il driver JDBC usando
una URL scritta all’interno dell’applicazione, la classe DriverManager
tenta di caricare i le classe dei driver che sono referenziate nella proprietò
di sistema jdbc.drivers
DataSource : l’applicazione usa questa interfaccia per richiedere l’oggetto
ad un name service provider . Questo permette all’applicazione di
disinteressarsi dei dettagli della connessione ( driver, username, password,
settaggi del driver … )
Per poter stabilire una connessione c’è bisogno di compiere due
passi :



Caricare il driver
Stabilire la connessione
Stabilire una connessione
Caricare il driver


Il caricamento del driver è, fondamentalmente, un’operazione da una riga
di codice; la classe che rappresenta il driver viene instanziata e questa
viene registrata all’interno della lista dei drivers disponibili nella VM
Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
Si richiede esplicitamente al class loader di caricare una classe passandogli il
riferimento completo <package>.<class name>
Stabilire una connessione
Uso di DriverManager

La classe lavora in congiunzione con l’interfaccia Driver : legge l’url,
riconosce il “subprotocol” e tenta di trovare un driver, disponibile tra i
drivers caricati, adatto a gestire quel protocollo.
Una volta caricato il driver, si usa il metodo getConnection per recuperare
la connessione al database


Connection conn = DriverManager.getConnection(<URL>);
L’url è una stringa composta da :

protocollo:sotto-protocollo:database-name[lista di proprietà]


protocollo : nel nostro caso jdbc



sotto-protocollo : è il “nome” con cui viene registrato il driver JDBC
nome-database : nome del database a cui connettersi
[lista di proprietà] : lista di proprietà della connessione
Es :
jdbc:derby:COFFEES
jdbc:oracle:thin:LILLY_IT_USER/[email protected]:1521:DEV165
Stabilire una connessione
Uso di DriverManager


Il metodo getConnection supporta anche il passaggio di username e
password.
String url = "jdbc:derby:Fred";
Connection con = DriverManager.getConnection(url, “utente", “password");

Se l’url viene riconosciuta, la classe DriverManager si occupa di gestire
tutti i dettagli della connessione e restituisce una connessione aperta al
database da cui è possibile creare statements, stored procedures recuperare
dati etc etc.
Stabilire una connessione
Uso di DataSource :


L’uso della classe DataSource aumenta le capacità di portabilità di una
applicazione rendendo possibile l’uso di un nome logico piuttosto che
definire I dettagli della connessione
InitialContext ic = new InitialContext() ;
DataSource ds = ic.lookup("java:comp/env/jdbc/myDB");
Connection con = ds.getConnection();

Oppure, per riflettere le stesse caratteristiche di DriverManager
InitialContext ic = new InitialContext() ;
DataSource ds = (DataSource) org.apache.derby.jdbc.ClientDataSource();
ds.setPort(1527);
ds.setHost("localhost");
ds.setUser("APP");
ds.setPassword("APP");
Connection con = ds.getConnection();
Recuperare i valori
Il risultato di una query SQL è, normalmente, una tabella. L’ API
JDBC mette a disposizione una interfaccia , ResultSet, che serve
a scorrere e a recuperare i valori ritornati dal database
Esistono tre tipi di ResultSet :





TYPE_FORWARD_ONLY : il ResultSet non è modificabile e lo si può scorrere solo in
avanti, da “prima della prima riga” a “dopo l’ultima riga” . Il contenuto del ResultSet
dipende dal modo in cui I dati vengono generati dal database : snapshot al momento
della select o valore al momento della fetch
TYPE_SCROLL_INSENSITIVE : il ResultSet è scrollabile in avanti e indietro di una
posizione rispetto alla riga corrente ed è permesso il posizionamento ad una
posizione assoluta
TYPE_SCROLL_SENSITIVE : il ResultSet è scrollabile in avanti e indietro di una
posizione rispetto alla riga corrente ed è permesso il posizionamento ad una
posizione assoluta. In aggiunta, il ResultSet è sensibile al cambiamento dei dati fatti
in un altra sessione … ma bisogna rileggere il record
Recuperare i valori

Esempio
Statement stmt = con.createStatement(
ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY
);
ResultSet srs = stmt.executeQuery(
"SELECT COF_NAME, PRICE FROM COFFEES
");
Recuperare i valori

I metodi di ResultSet per il posizionamento:

next() – muove il cursore avanti di una riga.Ritorna true se si posiziona su
una riga o false se si posiziona dopo l’ultima riga

previous() - muove il cursore indietro di una riga.Ritorna true se si
posiziona su una riga o false se si posiziona prima della prima riga.

first() – posiziona il cursore sulla prima riga. Ritorna true se il cursore è
sulla prima riga, false se il ResultSet non contiene righe

last() – posiziona il cursore sull’ultima riga. Ritorna true se il cursore è
posizionato sull’utlima riga, false se il ResultSet non contiene righe
beforeFirst() – posiziona il cursore all’inizio del ResultSet, prima della
prima riga.Se il ResultSet è vuoto il metodo non ha effetto.

afterLast() - posiziona il cursore alla fine del ResultSet, dopo l’ultima
riga.Se il ResultSet è vuoto il metodo non ha effetto.

relative(int rows) – muove il cursore di n righe, relativamente alla
posizione corrente

absolute(int row) – muove il cursore alla row-esima riga.
Recuperare i valori

Esempi

Sia srs un ResultSet con 500 righe
srs.absolute(-4); // posiziona alla riga 497
srs.absolute(4); // posiziona alla riga 4
srs.relative(-3); // posiziona alla riga 1
srs.relative(2); // posiziona alla riga 3
srs.absolute(4);
int rowNum = srs.getRow(); // rowNum = 4
srs.relative(-3);
int rowNum = srs.getRow(); // rowNum = 1
srs.relative(2);
int rowNum = srs.getRow(); // rowNum = 3
Recuperare i valori


Esistono altri quattro metodi per verificare le posizioni
“chiave” di un cursore :
 isFirst
 isLast
 isBeforeFirst
 isAfterLast
Normalmente vengono utilizzati all’interno di loop
Recuperare i valori
I metodi di ResultSet per la lettura:



L’interfaccia ResultSet dichiara i “getters” ( getInt,
getBoolean, getObject … )
Le colonne si leggono :




Per nome
Per posizione ( comincia da 1 )
Se due colonne o più colonne hanno lo stesso nome
viene letto il valore della colonna “più a sinistra”
Per una maggiore portabilità, non leggere mai la
colonna due volte
Recuperare i valori

Esempio :
Statement stmt = con.createStatement(
ResultSet.TYPE_SCROLL_SENSITIVE,
ResultSet.CONCUR_READ_ONLY
);
ResultSet srs = stmt.executeQuery(
"SELECT COF_NAME, PRICE FROM COFFEES“
);
while (srs.next()) {
String name = srs.getString("COF_NAME");
float price = srs.getFloat("PRICE");
System.out.println(name + " " + price);
}
Recuperare i valori

Esempio :
Statement stmt = con.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY
);
ResultSet srs = stmt.executeQuery(
"SELECT COF_NAME, PRICE FROM COFFEES“
);
srs.afterLast();
while (srs.previous()) {
String name = srs.getString("COF_NAME");
float price = srs.getFloat("PRICE");
System.out.println(name + " " + price);
}
Recuperare i valori

Esempio reale:
try {
Vector results = new Vector();
Connection con = DriverManager.getConnection(url,
"myLogin", "myPassword");
Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(query);
while (rs.next()) {
String s = rs.getString("COF_NAME");
float f = rs.getFloat("PRICE");
String text = s + " " + f;
results.addElement(text);
}
stmt.close();
con.close();
setResults(results);
} catch(SQLException ex) {
setError("SQLException: " + ex);
}
Aggiornare i valori
ResultSet :


Set + Update


Aggiorna una riga alla volta
Il metodo cancelUpdates() cancella tutte le update
fatte a partire dall’ultimo updateRow();
Statement stmt = conn.createStatement(
ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE
);
ResultSet srs = stmt.executeQuery(
"select COF_Name from COFFEES where price = 7.99“
);
…
srs.next();
srs.updateString("COF_NAME", "Foldgers");
srs.updateRow();
PreparedStatements
Sono oggetti Statement (subclasses) che vengono precompilati dal database ( = + veloci
)
Sono molto utili quando vengono parametrizzati ed usati all’interno di un loop
Si creano allo stesso modo di Statement:




PreparedStatement updateSales = con.prepareStatement(
"UPDATE COFFEES SET SALES = ? WHERE COF_NAME LIKE ?“
);
I valori vengono passati utilizzando I setter appropriati : setXXX(posizione, valore) ;


updateSales.setInt(1, 75);
updateSales.setString(2, "Colombian");
updateSales.executeUpdate();
Lo statement, compilato, precedente è equivalente a

stmt.executeUpdate(
"UPDATE COFFEES SET SALES = 75 WHERE COF_NAME LIKE 'Colombian'“
);
PreparedStatements
Ma in un loop l’uso di PreparedStatement rende il compito + semplice :
PreparedStatement updateSales;
String updateString = "update COFFEES " + "set SALES = ? where COF_NAME like ?";
updateSales = con.prepareStatement(updateString);
int [] salesForWeek = {175, 150, 60, 155, 90};
String [] coffees = {"Colombian", "French_Roast", "Espresso", "Colombian_Decaf",
"French_Roast_Decaf"};
int len = coffees.length;
for(int i = 0; i < len; i++) {
updateSales.setInt(1, salesForWeek[i]);
updateSales.setString(2, coffees[i]);
updateSales.executeUpdate();
}
Transazioni


L’API JDBC supporta le transazioni :

Per default c’è l’autocommit : ad ogni update o delete viene fatta la commit

Si abilita/disabilita l’autocommit con setAutoCommit(boolean valore)

Esistono i metodi commit() e rollback() per chiudere o cancellare una transazione
Dalla versione 3 della JDBC API esistono i “savepoints” :

All’interno di una transazione posso avere delle “mini” transazioni
Statement stmt = conn.createStatement();
int rows = stmt.executeUpdate("INSERT INTO TAB1 (COL1) VALUES (?FIRST?)");
// set savepoint
Savepoint svpt1 = conn.setSavepoint("SAVEPOINT_1");
rows = stmt.executeUpdate("INSERT INTO TAB1 (COL1) " + "VALUES (?SECOND?)");
...
conn.rollback(svpt1);
...
conn.commit();


Viene inserita una riga, settato un savepoint ( svp1 ) e inserita una riga. La
rollback arriva fino a svp1 e la commit chiude la transazione inserendo la riga
1
Quando si rilascia un savepoint, il suo utilizzo lancia una SQLException
Stored Procedures

L’API JDBC supporta le stored procedures
Si può creare una stored procedure usando uno statement SQL .

Le stored procedures si usano tramite gli oggetti “CallableStatements”

String createProcedure = "create procedure SHOW_SUPPLIERS as "
+ "select SUPPLIERS.SUP_NAME, COFFEES.COF_NAME "
+ "from SUPPLIERS, COFFEES where SUPPLIERS.SUP_ID = COFFEES.SUP_ID "
+ "order by SUP_NAME";
Statement stmt = con.createStatement();
stmt.executeUpdate(createProcedure);
CallableStatement cs = con.prepareCall("{call SHOW_SUPPLIERS}");
ResultSet rs = cs.executeQuery();
Scarica

Corso Java : Lezione 4