basi di dati e sistemi informativi sql per applicazioni home page del corso: difelice/dbsi
TRANSCRIPT
Basi di Dati e Sistemi
Informativi
SQL per ApplicazioniHome page del corso:
http://www.cs.unibo.it/~difelice/dbsi/
SQL per Applicazioni
L’accesso tipico ad una base di dati avviene mediante applicazioni integrate nel sistema informativo.
Integrazione di SQL in linguaggi di programmazione ad alto livello.
Integrazione di SQL in applicazioni/interfacce Web (Web Information System, WIS)
SQL per Applicazioni
L’integrazione del linguaggio SQL con i normali linguaggi di programmazione di alto livello (C, C++,
Java, etc) presenta alcuni ostacoli.
Differenze di sintassi tra i linguaggi.
Differenze nel meccanismo di gestione dei dati (SQL e’ set-oriented, mentre i linguaggi di programmazione sono per lo piu’ tuple-oriented).
SQL per Applicazioni
Due soluzioni per consentire l’uso di SQL all’interno di un programma scritto in un linguaggio di programmazione:
SQL Embedded il programma sorgente contiene codice misto (es. SQL e Java)
Call Level Interface (CLI) il programma sorgente contiene chiamate ad una libreria per la gestione dei dati (es. JDBC).
SQL per Applicazioni
SQL Embedded Il programma contiene istruzioni SQL, distinte dalle istruzioni del linguaggio sorgente attraverso l’uso di separatori (exec sql e ;)
In fase di compilazione, il preprocessore riconosce le istruzioni SQL e le sostituisce con opportune chiamate di libreria del DBMS.
SQL per Applicazioni
main() {int i;…exec sql
insert into Impiegati
values(“Marco”,20);…
}
PREPROCESSORE
LIBRERIAACCESSO
DBMS
DBMS
CHIAMATA LIBRERIA
SQL per Applicazioni
Il preprocessore esegue i seguenti task:
Verifica la sintassi del codice SQL
Sostituisce il codice SQL con le rispettive chiamate di libreria del DBMS.
Necessario un preprocessore specifico per una specifica combinazione DMBS-linguaggio-piattaforma (es. Oracle/C/Linux).
SQL per Applicazioni#include<stdlib.h>main(){ exec sql begin declare section; char *NomeDip = "Manutenzione"; char *CittaDip = "Pisa"; int NumeroDip = 20; exec sql end declare section; exec sql connect to utente@librobd; if (sqlca.sqlcode != 0) { printf("Connessione al DB non riuscita\n"); } else { exec sql insert into Dipartimento values(:NomeDip,:CittaDip,:NumeroDip); exec sql disconnect all; }}
C con SQL Embedded
SQL per Applicazioni#include<stdlib.h>main(){ exec sql begin declare section; char *NomeDip = "Manutenzione"; char *CittaDip = "Pisa"; int NumeroDip = 20; exec sql end declare section; exec sql connect to utente@librobd; if (sqlca.sqlcode != 0) { printf("Connessione al DB non riuscita\n"); } else { exec sql insert into Dipartimento values(:NomeDip,:CittaDip,:NumeroDip); exec sql disconnect all; }}
C con SQL Embedded
Variabili C condivise
SQL per Applicazioni#include<stdlib.h>main(){ exec sql begin declare section; char *NomeDip = "Manutenzione"; char *CittaDip = "Pisa"; int NumeroDip = 20; exec sql end declare section; exec sql connect to utente@librobd; if (sqlca.sqlcode != 0) { printf("Connessione al DB non riuscita\n"); } else { exec sql insert into Dipartimento values(:NomeDip,:CittaDip,:NumeroDip); exec sql disconnect all; }}
C con SQL Embedded
Struttura dati condivisa
SQL per Applicazioni#include<stdlib.h>main(){ exec sql begin declare section; char *NomeDip = "Manutenzione"; char *CittaDip = "Pisa"; int NumeroDip = 20; exec sql end declare section; exec sql connect to utente@librobd; if (sqlca.sqlcode != 0) { printf("Connessione al DB non riuscita\n"); } else { exec sql insert into Dipartimento values(:NomeDip,:CittaDip,:NumeroDip); exec sql disconnect all; }}
C con SQL Embedded
Esecuzione della query
SQL per Applicazioni
int main() { exec sql connect to universita user pguser identified by pguser; exec sql create table studente (matricola integer primary key, nome varchar(20), annodicorso integer); exec sql disconnect; return 0;}
CODICE SORGENTE
SQL per Applicazioni
#include <ecpgtype.h>
#include <ecpglib.h>
#include <ecpgerrno.h>
#include <sqlca.h>
int main() {
ECPGconnect(__LINE__, "universita" , "pguser" , "pguser" , NULL, 0);
ECPGdo(__LINE__, NULL, "create table studente ( matricola integer primary key , nome varchar ( 20 ) , annodicorso integer )", ECPGt_EOIT, ECPGt_EORT);
ECPGdisconnect(__LINE__, "CURRENT");
return 0;
}
CODICE PRE-COMPILATO
SQL per Applicazioni
Conflitto d’impedenza sui dati:
SQL e’ un linguaggio set-oriented, un’interrogazione puo’ restituire come risultato un’intera tabella (es. SELECT).
I linguaggi di programmazione accedono agli elementi di una struttura dati scandendo le righe una per volta.
SOLUZIONE: Usare i cursori …
SQL per Applicazioni
Cursore Iteratore per accedere alla righe di una tabella una alla volta.
CODICESORGENT
E
DBMSSELECT
BUFFER delPROGRAMMA
CU
RS
OR
E
Il cursore puo’ muoversi avanti/indietro sulle righe della tabella.
SQL per Applicazioni
Definizione del cursore
declare NomeCursore [scroll] cursor for Select SQLCode
declare NomeCursore scroll cursor for SELECT Cognome, Stipendio
FROM ImpiegatiWHERE (Stipendio > 1000)
SQL per Applicazioni
Esecuzione delle interogazione
open NomeCursore
Deallocazione delle interogazione
close NomeCursore
Recupero dei risultati
fetch NomeCursore into ListaVariabili
SQL per Applicazionivoid VisualizzaStipendiDipart(char NomeDip[]){
char Nome[20], Cognome[20];long int Stipendio;$ declare ImpDip cursor for
select Nome, Cognome, Stipendiofrom Impiegatowhere Dipart = :NomeDip;
printf("Dipartimento %s\n",NomeDip);$ open ImpDip;$ fetch ImpDip into :Nome, :Cognome, :Stipendio;while (sqlcode == 0){
printf("Attuale stipendio: %d\n",Stipendio);$ fetch ImpDip into :Nome, :Cognome, :Stipendio;
}$ close cursor ImpDip;
}
SQL per Applicazioni
SQLJ Tecnica per includere codice SQL all’interno di programmi Java.import java.sql.*;
public class SimpleDemoSQLJ { public Address getEmployeeAddress(int empno) throws SQLException { Address addr; #sql { SELECT office_addr INTO :addr FROM employees WHERE empnumber = :empno }; return addr; }}
SQL per Applicazioni
Tecnica vista fin qui: Static SQL Embedded. PROBLEMA: Non sempre le istruzioni
SQL da eseguire sono note quando si scrive il programma …
Dynamic SQL Embedded Tecnica che permette di eseguire istruzioni SQL costruite dal programma (o addirittura ricevute dal programma attraverso parametri o da input).
SQL per Applicazioni
Due soluzioni per consentire l’uso di SQL all’interno di un programma scritto in un linguaggio di programmazione:
SQL Embedded il programma sorgente contiene codice misto (es. SQL e Java)
Call Level Interface (CLI) il programma sorgente contiene chiamate ad una libreria per la gestione dei dati (es. JDBC).
SQL per Applicazioni
Nella modalita’ CLI, il linguaggio sorgente offre una libreria specifica per interagire con un DBMS (indipendente dal DBMS).
1. Si crea una connessione con il DBMS.2. Si invia un comando SQL con la
richiesta.3. Si riceve la risposta dal DBMS, e la si
gestisce per mezzo di cursori.4. Si chiude la connessione con il DBMS.
INTERAZIONE
SQL per Applicazioni
Sono disponibili diverse tecnologie di CLI per piattaforme/linguaggi differenti…
Tecnologia Piattaforma
ODBC Interfaccia standard, multi-piattaforma
OLE DB Microsoft, basata sul modello .COM
ADO Microsoft, interfaccia record-oriented
ADO.NET Microsoft, estensione ADO per piattaforma .NET
JDBC Interfaccia Java, multi-piattaforma
SQL per Applicazioni
Java Database Connectivity (JDBC) insieme di librerie Java per accesso ai dati di un database relazionale, indipendenti dallo specifico DBMS in uso.
JDBC denota un’interfaccia standard, implementata da classi dette driver.
Un driver e’ specifico di un certo DBMS (es. MySQL Connector/J per MySQL)
SQL per Applicazioni
JDBC puo’ essere usato in 4 architetture:1. Bridge JDBC-ODBC: si richiama un driver
ODBC, che deve essere disponibile sul client.
2. Driver nativo sul client: si richiama un componente proprietario (non necessariamente Java) sul client.
3. Driver puro Java con server intermedio ("middleware server"): si comunica via protocollo di rete con il server intermedio, che non deve risiedere sul client.
4. Driver puro Java, con connessione al DBMS: si interagisce direttamente con il DBMS.
SQL per Applicazioni
SQL per Applicazioni
Vantaggi di SQLJ su JDBC:
SQLJ richiede meno codice. La sintassi delle istruzioni SQL puo’
essere controllata durante la compilazione
Vantaggi di JDBC su SQLJ:
SQLJ richiede un preprocessore, non sempre disponibile su tutte le piattaforme/DBMS.
SQL per Applicazioni
Le API di JDBC (java.sql) consentono di:
Creare la connessione con un DBMS (supponendo di aver gia’ installato i driver).
Eseguire query SQL sul DBMS.
Processare il risultato della query SQL.
Gestire transazioni ed operazioni batch.
SQL per Applicazioni
Per creare una connessione, sono disponibili le seguenti classi:
Driver interfaccia di collegamento verso il DBMS (implementato da ogni DBMS).
DriverManager servizio di base per la gestione dei drivers attualmente presenti.
Connection sessione di connessione ad uno specifico DBMS, il cui driver e’ gestito dal DriverManager.
SQL per Applicazioni
Supponendo di avere i driver del DBMS (es. MySQL) installati sul proprio host, il passo successivo e’ creare la connessione dal DriverManager.
static Connection getConnection(String url, String user, String password)
url URL del DB, tipo e nome. user, password credenziali
d’accesso.
SQL per Applicazioni
Supponendo di avere i driver del DBMS (es. MySQL) installati sul proprio host, il passo successivo e’ creare la connessione dal DriverManager.
String url=“jdbc:mysql://localhost:3306/provadb”;String user=“root”;String password=“root”;Connection con=DriverManager.getConnection(url, user, password);
SQL per Applicazioni
Per eseguire una query e processarne il risultato, sono disponibili le seguenti classi:
Connection sessione di connessione ad uno specifico DBMS, il cui driver e’ gestito dal DriverManager.
Statement componente usata per eseguire una query SQL su una Connection.
ResultSet tabella di dati che contiene il risultato di esecuzione di uno Statement.
SQL per Applicazioni
Una volta creata la Connection, e’ possibile creare uno Statement associato, e quindi eseguire una query sul DBMS.
Connection con=DriverManager.getConnection(url, user, password);
Statement state= con.createStatement();
String sql=“ SELECT * FROM IMPIEGATI”;
ResultSet rs=state.executeQuery(sql);
SQL per Applicazioni
Per gestire il risultato di una query, si utilizza la classe ResultSet tabella di dati generata dall’esecuzione di una query SQL + cursore.
ResultSet mantiene un puntatore alla riga corrente della tabella.
Tramite il metodo next() e’ possibile spostarsi alla riga successiva; next() ritorna falso quando non ci sono piu’ righe nel ResultSet.
SQL per Applicazioni
Per gestire il risultato di una query, si utilizza la classe ResultSet tabella di dati generata dall’esecuzione di una query SQL + cursore.
ResultSet mantiene un puntatore alla riga corrente della tabella.
Tramite il metodo next() e’ possibile spostarsi alla riga successiva; next() ritorna falso quando non ci sono piu’ righe nel ResultSet.
SQL per Applicazioni
Data una riga del ResultSet, si possono utilizzare metodi tipizzati per accedere ad una specifica colonna, conoscendone l’indice:
String getString(int columnIndex) boolean getBoolean(int
columnIndex) int getInt(int columnIndex) float getFloat(int columnIndex) Date getDate(int columnIndex) …
SQL per Applicazioni
Data una riga del ResultSet, si possono utilizzare metodi tipizzati per accedere ad una specifica colonna, conoscendone l’indice:
Statement st=con.createStatement();ResultSet rs=st.executeQuery(“SELECT NOME, STIPENDIO FROM IMPIEGATI”);while(rs.next) {
System.out.println(rs.getString(1));System.out.println(rs.getInt());
}
SQL per Applicazioni
Eseguire una query senza un controllo preventivo sui parametri forniti dall’utente puo’ essere pericoloso dal punto di vista della sicurezza (es. SQL Injection); per questo, si preferisce:
Preparare lo Statement inserendo dei placeholder al posto dei parametri della query.
Fornire i valori dei parametri, ed eseguire la query (in maniera sicura).
SQL per Applicazioni
Esempio di esecuzione di una query SQL mediante PreparedStatement (anziche’ Statement).
PreparedStatement pst;String author=“Michele”;pst=con.PreparedStatement(“INSERT INTO IMPIEGATI(Nome) VALUES(?)”);
pst.setString(1, author);pst.executeUpdate();
SQL per Applicazioni
In Java JDBC, una transazione indica un insieme di comandi SQL cui puo’ essere associata una modalita’ di esecuzione del “tutto o niente”, ossia:
Tutti i comandi della transazione sono stati eseguiti correttamente, OPPURE:
In caso di errori, e’ possibile fare rollback dell’esecuzione di tutti comandi della transazione.
SQL per Applicazioni
Per costruire una transazione in JDBC: Si costruiscono le query, e si eseguono
mediante executeUpdate() o executeQuery().
Si utilizza il metodo commit() del Connection per eseguire le operazioni sul DBMS in maniera persistente.
Si utilizza il metodo rollback() del Connection per annullare le operazioni sul DBMS eseguite dopo la precedente commit.
SQL per Applicazioni
try {st.executeupdate(“UPDATE IMPIEGATO
SET Name=“Marco” WHERE Name=“Michele”);
st.executeupdate(“UPDATE IMPIEGATO SET Name=“Mario” WHERE Name=“Giovanni”);
con.commit();}catch(SQLException) {
con.rollback();}
SQL per Applicazioni
Per poter lavorare con le transazioni in JDBC, e’ necessario impostare a false la modalita’ di autocommit dei comandi SQL.
Connection con=DriverManager(url. user, password);con.setAutoCommit(false);
Se si utilizza l’autocommit, non e’ possibile fare rollback delle transazioni …
SQL per Applicazioni
Nel caso si debbano eseguire operazioni di DELETE, UPDATE, INSERT in serie, e’ possibile usare il meccanismo dei batch update in JDBC:
Le query sono raggruppate in un unica query, che viene inviata al DMBS (vantaggi in terminimi di efficienza di esecuzione).
Il batch restituisce un array con il numero di righe affette da ciascuna query.
SQL per Applicazioni
Nel caso si debbano eseguire operazioni di DELETE, UPDATE, INSERT in serie, e’ possibile usare il meccanismo dei batch update in JDBC:
addBatch(String sql) aggiunge una query SQL all’insieme batch.
executeBatch() esegue il batch update, ritorna un array di dimensione pari alle query SQL, con il numero di righe affette da ciascuna.
SQL per Applicazioni
st=con.createStatement();
st.addBatch(“DELETE FROM Impiegati”);st.addBatch(“INSERT INTO IMPIEGATI(Nome) VALUES(‘Marco’”);st.addBatch(“INSERT INTO IMPIEGATI(Nome) VALUES(‘Michele’”);
int counts=st.executeBatch();con.commit();
SQL per Applicazioni
st=con.createStatement();
st.addBatch(“DELETE FROM Impiegati”);st.addBatch(“INSERT INTO IMPIEGATI(Nome) VALUES(‘Marco’”);st.addBatch(“INSERT INTO IMPIEGATI(Nome) VALUES(‘Michele’”);
int counts=st.executeBatch();con.commit();
SQL per Applicazioni
In un DBMS, le informazioni sul modello logico di rappresentazione di una tabella si dicono metadati, e sono rappresentati a sua volta tramite tabelle.
Nomi delle tabelle Nomi degli attributi di una tabella Tipi degli attributi Vincoli relazionali …
ESEMPI DI METADATI
SQL per Applicazioni
In JDBC, e’ possibile accedere al campo metadati di un ResultSet tramite il metodo getMetaData().
PreparedStatement pst;pst=con.PreparedStatement(“SELECT Nome, Cognome FROM IMPIEGATI”);ResultSet rs=pst.executeQuery();ResultSetMetaData meta=pst.getMetaData();String column1=meta.getColumnName(1);String column2=meta.getColumnName(2);