La programmazione
Angelo Chianese, Vincenzo Moscato,
Antonio Picariello, Lucio Sansone
Basi di dati per la gestione
dell'informazione 2/ed
McGraw-Hill
Capitolo 8
Appunti dalle lezioni
Sistemi informativi
e basi di dati
La Progettazione
Concettuale
Il modello relazionale
La Progettazione
Logica
Utilizzo di un
DBMS Reale
La Progettazione
Fisica
SQL come DDL
SQL come DML
SQL come DCL
Strumenti CASE
Forme normali
Programmazione
Transazioni e
tecnologie di supporto
Basi di dati
direzionali
Basi di dati
distribuite
1
Uso diretto
SQL
Linguaggi
ad hoc
Stored Procedures
Triggers
ODBC
In linguaggi
“classici”
Call Level
Interface
JDBC
SQL-embedded
I comandi SQL sono immersi direttamente nel
linguaggio ospite.
Un precompilatore o preprocessore esamina prima di
tutto il codice sorgente del programma per identificare
le istruzioni di interazione con la base di dati ed
estrarle per l’elaborazione con il DBMS.
Queste vengono sostituite nel programma da
chiamate di funzione al codice generato dal DBMS.
La comunicazione dei risultati elaborativi del DBMS
all’applicazione avviene a mezzo di apposite variabili
condivise (e.g., SQLCODE, SQLSTATE), mentre le
variabili del programma possono essere usate come
parametri nelle istruzioni SQL
2
ESEMPIO
EXEC SQL BEGIN DECLARE SECTION;
varchar nome [50];
varchar cognome [50];
varchar matricola [10];
int SQLCODE;
EXEC SQL END DECLARE SECTION;
printf(’’Immettere matricola: ’’);
scanf(’’%s’’,&matricola);
EXEC SQL;
SELECT NOME, COGNOME into :nome, :cognome
FROM STUDENTI
WHERE MATRICOLA=:matricola;
if (SQLCODE==0)
printf(’’Studente: %s %s\n’’, nome, cognome);
else printf(’’Matricola inesistente’’);
SQL-embedded
Il linguaggio SQL-embedded risulta un approccio di
programmazione delle basi di dati statico:
il testo dell’interrogazione è scritto all’interno del
programma e non può essere cambiato senza
ricompilare o rielaborare il codice sorgente
Inoltre poiché ogni applicazione per interfacciarsi ad
un DBMS fa riferimento a delle proprie librerie (di
solito scritte C), se questa ultima deve utilizzare per
qualche ragione un nuovo database, diverso dal
precedente, occorre riscrivere tutto il codice di
gestione dei dati.
3
SQL/CLI (Call Level Interface):
Apposite libreria di funzioni sono rese
disponibili al linguaggio ospite per
l’interfacciamento alla base di dati.
Vi sono funzioni per la connessione al
database, per l’esecuzione di
interrogazione, per l’aggiornamento
dei dati, e così via.
Accesso mediante SQL/CLI
Standard a “livello di chiamata”
Approccio dinamico per la programmazione
delle basi di dati:
Per l’accesso alla base di dati viene usata una
libreria di funzioni che da un lato fornisce
maggiore flessibilità e non richiede la presenza
di alcun preprocessore
Comporta, però, che la verifica della sintassi e
altri controlli sui comandi SQL avvenga solo al
momento dell’esecuzione del programma.
4
SQl/CLI
Basandosi sullo
standard SQL/CLI, ogni
DBMS mette a
disposizione apposite
interfacce di
programmazione (API)
per i principali
linguaggi che
permettevano alle
applicazioni scritte in
quel linguaggio di
“interrogare”
ODBC
Col passare degli anni nasce poi l’esigenza
di standardizzare l’interfaccia attraverso la
quale un’applicazione poteva accedere ad
una qualsiasi base di dati relazionale
garantendo l’interoperabilità.
Nel 1991 la Microsoft propone uno standard
per la comunicazione con database remoti
noto col nome di ODBC (Open Database
Connectivity).
5
ODBC
Nell’architettura ODBC il collegamento tra
un’applicazione client e il server della base di dati,
richiede l’uso di un driver, ovvero di una libreria che
viene collegata dinamicamente all’applicazione e da essa
invocata quando si vuole accedere alla base di dati.
Ogni driver maschera le differenza di interazione legate
non solo al DBMS, ma (in parte) anche al sistema
operativo e ai protocolli di rete utilizzati.
I driver astraggono dai protocolli specifici dei produttori
dei DBMS, fornendo un’interfaccia di programmazione
delle applicazioni comune ai client del database
ODBC
L’ applicazione
Il driver manager
I driver
La sorgente dati
o data source
Application
ODBC driver
manager
Driver
(DBMS/OS/network)
Data Source
6
Esempio ODBC
#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <sql.h>
#include <sqlext.h>
#define MAX_DATA 100
void main() {
HENV henv;
HDBC hdbc;
HSTMT hstmt;
RETCODE rc;
// Environment handle
// Connection handle
// Statement handle
// Return code for ODBC functions
SQLAllocEnv(&henv);
SQLAllocConnect(henv, &hdbc);
rc = SQLConnect(hdbc, (unsigned char *) "esempioODBC", SQL_NTS, NULL,
SQL_NTS, NULL, SQL_NTS);
SQLAllocStmt(hdbc, &hstmt);
if ((rc != SQL_SUCCESS) && (rc != SQL_SUCCESS_WITH_INFO)) {
printf("Connessione non riuscita!\n");
exit(0);
}
printf("Connessione riuscita!\n");
7
Esempio ODBC
rc=SQLExecDirect(hstmt, (unsigned char *) "select * from
studenti", SQL_NTS);
if ((rc != SQL_SUCCESS) && (rc != SQL_SUCCESS_WITH_INFO)) {
/*Qui va il codice per la gestione dell'errore*/
printf("Query non riuscita!\n");
exit(0);
}
printf("Query eseguita correttamente!\n");
short int cols;
/*Numero di colonne nel risultato*/
rc = SQLNumResultCols(hstmt,&cols);
if ((rc != SQL_SUCCESS) && (rc != SQL_SUCCESS_WITH_INFO)) {
printf("Impossibile estrarre il numero di colonne!\n");
exit(0);
}
printf("%d colonne nel risultato\n",cols);
Esempio ODBC
rc1 = SQLFetch(hstmt);
printf("\nRisultato della query\n");
while (rc1 == SQL_SUCCESS) {
int i=1;
while ((rc2 = SQLGetData(hstmt, i, SQL_CHAR, szData,
MAX_DATA - 1,&cbData)) == SQL_SUCCESS) {
if (cbData > 0) printf("%s\t", szData);
else printf("NULL\t", szData);
i++;
}
rc1 = SQLFetch(hstmt);
printf("\n");
}
printf("\n");
SQLFreeStmt(hstmt, SQL_DROP);
SQLDisconnect(hdbc);
SQLFreeConnect(hdbc);
SQLFreeEnv(henv);
}
8
JDBC
JDBC é un interfaccia di programmazione (API) ad
oggetti basata sullo standard CLI e definita dalla
Sun Microsystems per l’accesso ad un database in
maniera indipendente dalla piattaforma.
JDBC è divenuto oramai uno “standard de
facto” per lo sviluppo di applicazioni JAVA “DBoriented” e fa parte del pacchetto software JDK
dalla versione 1.1.
Vantaggi:
Astrazione dal DB
Riuso
Sintassi semplice
http://download.oracle.com/javase/tutorial/jdbc/index.html
9
4 passi fondamentali
Apertura della connessione
Esecuzione di comandi SQL
Elaborazione dei risultati
Chiusura della connessione
10
JDBC: 4 Tipi
Tipo 1:
Tipo 2:
Scritti in parte in Java ed in parte in un altro linguaggio.
Di fatto usano librerie native delDBMS (scritte ad esempio in
C)
Oracle's OCI (Oracle Call Interface).
Tipo 3
Implementano un mapping verso un’altra API
JDBC-ODBC
Usano un middleware server con un protocollo indipendente
dal database.
Tipo 4:
Sono scritti completamente in JAVA
Il driver JDBC PostgreSQL
Esempio JDBC:ODBC
import java.sql.*;
public class JDBCApp {
public static void main (String args[]) {
try {
String driver = "sun.jdbc.odbc.JdbcOdbcDriver";
Class.forName(driver);
Creiamo la stringa di connessione
String url = "jdbc:odbc:myDataSource";
Connection con =
DriverManager.getConnection (url, "myUserName", "myPassword");
Statement cmd = con.createStatement ();
String qry = "SELECT * FROM myTable";
ResultSet res = cmd.executeQuery(qry);
while (res.next()) {
System.out.println(res.getString("columnName1"));
System.out.println(res.getString("columnName2"));
}
res.close();
cmd.close();
con.close();
} catch (SQLException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}}}
11
il package java.sql
Interfaccia Driver
rappresenta il punto di partenza per ottenere una
connessione ad un DBMS. I produttori di driver JDBC
devono implementare un’interfaccia Driver (mediante
un’opportuna classe) affinchè un’applicazione possa
interfacciarsi con un tipo particolare di DBMS.
Classe DriverManager
è la classe che contiene tutti i driver di database
registrati
Interfaccia Connection
un oggetto di tipo Connection rappresenta una
connessione attiva ad un database
12
il package java.sql.
Interfaccia Statement:
un oggetto di tipo Statement viene utilizzato per inviare
query semplici SQL, che non fanno uso di parametri
Interfaccia PreparedStatement
un oggetto di tipo PreparedStatement viene utilizzato per
inviare query che presentano parametri di input.
Interfaccia CallableStatement
usato per costruire query parametriche con parametri di
input e output. Consente di eseguire anche stored
procedure memorizzate sul server.
Interfaccia ResultSet
un oggetto ResultSet rappresenta il risultato di una query
di selezione (insieme di record o tuple).
Stringa di connessione
In JDBC ogni database viene
univocamente identificato da una
particolare stringa di connessione
detta JDBC URL.
Tale stringa adotta un formalismo
simile alla definizione degli URL e
serve a definire dove si trova e come
accedere al database.
jdbc:<driver>:<database>
13
Stringa di connessione
In connessioni di tipo JDBC-ODBC il
terzo termine coincide con il nome
della sorgente dati utente o di
sistema
jdbc:odbc:dbsegreteria
Stringa di connessione
Nel caso di connessioni dirette con
driver java puri, il terzo termine
invece definisce i parametri di
accesso dell’istanza di database.
jdbc:oracle:thin:@db.unina.it:1521:dbsegreteria
jdbc:postgres://localhost/DISCHI
14
Nota bene
L’operazione di connessione “costa” in
termini di tempo
Per questo solitamente si usa,
all’inizio dell’applicazione, aprire tutte
le connessioni necessarie per poi
chiuderle alla fine dell’applicazione
Le operazioni vere e proprie sui DB
verranno fatte con altre operazioni
OGGETTO STATEMENT
Permette di fare rapidamente query
Connection connection= DriverManager.getConnection(url,"root","");
Statement stm = connection.CreateStatement();
ResultSet rst = stm.executeQuery(query);
Per interrogazioni
int a = stm.executeUpdate(query);
per statement di modifica (insert, update, delete) o
DDL (create table o drop table).
Viene restituito un numero intero (contatore di
aggiornamento) rappresentante il numero di righe
che sono state nserite/aggiornate/cancellate
il valore 0 per statement di tipo DDL.
15
Statement
Esiste anche il metodo execute()
Viene invocato quando non si è certi
della natura della query
Restituisce true nel caso vi siano delle
tuple di ritorno
Tali tuple sono reperibili invocando il
metodo getResult()
PreparedStatement
Permette di sottomettere comandi SQL che
prevedono la presenza di parametri
In questo modo la stesso comando SQL può
essere richiamate diverse volte con diversi valori
I parametri sono indicati con il simbolo ‘?’.
L’interfaccia PreparedStatement estende
l’interfaccia Statement ereditandone tutte
le funzionalità con dei metodi aggiuntivi per
gestione dei parametri.
16
PreparedStatement
Anche questa interfaccia prevede i metodi
executeQuery e executeUpdate per
l’esecuzione del comando SQL
Prima della loro invocazione devono essere
valorizzati i parametri attraverso i metodi
del tipo setXXX che ricevendo la coppia
nome valore contestualizzano la query
Il suffisso XXX indica un tipo di dato
primitivoin Java (int, float, double, ..)
PreparedStatement
String query =
’’INSERT INTO STUDENTI VALUES (?,?,?)’’;
Prepared Statament
pstmt=conn.prepareStatement(query);
pstmt.setString(1,’’010/000010’’);
pstmt.setString(2,’’Diego Armando’’);
pstmt.setString(3,’’Maradona’’);
rs=pstmt.executeUpdate();
pstmt.setString(1,’’010/000011’’);
pstmt.setString(2,’’Francesco’’);
pstmt.setString(3,’’Totti’’);
rs=pstmt.executeUpdate();
….
17
CallableStatement
Supporta la chiamata a stored procedure
È possibile utilizzarle solo quando il driver ammette
questo tipo di query
Utilizza il metodo Connection.prepareCall() (simile al
prepareStatement())
Ogni DBMS ha una propria sintassi per l’invocazione
delle stored procedure
In JDBC ho due sintassi standard
– {call procedure_name[(?[,?…])]}
{? = call procedure_name[(?[,?…])]}
L’associazione placeholder valori avviene come nelle
PreparedStatement
ResultSet
Rappresentano il mezzo per accedere alle
tuple risultato di una query
next()
previous()
first()
last()
beforeFirst()
afterLast()
absolute(int pos)
relative (int pos)
A secondo di come definisco il ResultSet
posso usare alcuni dei modi di spostamento
18
CreateStatement
public Statement createStatement()
throws SQLException
public Statement createStatement(int resultSetType,
int resultSetConcurrency)
throws SQLException
resultSetType
RestltSet.TYPE_FORWARD_ONLY
ResultSet.TYPE_SCROLL_INSENSITIVE
ResultSet.TYPE_SCROLL_SENSITIVE
resultSetConcurrency
ResultSet.CONCUR_READ_ONLY
ResultSet.CONCUR_UPDATABLE
ResultSet
ResultSet.TYPE_FORWARD_ONLY (default):
Permette solo la navigazione in un senso attraverso il metodo next().
identico al caso precedente ma sensibile alle modifiche alla base dati
ResultSet.CONCUR_READ_ONLY (default):
Si possono navigare in qualunque modo anche con accesso diretto.
Eventuali modifiche alla base dati fatte da altri utenti non saranno
visibili
ResultSet.TYPE_SCROLL_SENSITIVE
Solo rieseguendo la query io possono ritornare all’inizio.
ResultSet.TYPE_SCROLL_INSENSITIVE
il lock fatto sulle tuple è di tipo read e viene utilizzato solo nel caso il
cursore non sia modificabile
ResultSet.CONCUR_UPDATABLE:
il lock è di tipo write e quindi blocca accessi da parte di altri utenti
19
Esempio di modifica: Cancellazione
Attraverso il metodo
ResultSet.deleteRow() viene
cancellata la tupla al momento
puntata
rs.absolute(15);
rs.deleteRow();
Esempio di modifica: update
public void modifyPrices(float percentage) throws SQLException {
Statement stmt = null;
try {
stmt = con.createStatement();
stmt = con.createStatement(
ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATA
ResultSet uprs = stmt.executeQuery("SELECT * FROM COFFEES");
while (uprs.next()) {
float f = uprs.getFloat("PRICE");
uprs.updateFloat("PRICE", f * percentage);
uprs.updateRow();
}
} catch (SQLException e ) {
JDBCTutorialUtilities.printSQLException(e);
} finally {
if (stmt != null) { stmt.close(); }
}
}
20
Esempio di modifica: insert
21