Sei sulla pagina 1di 104

Sistemi informativi su Web

Persistenza - JDBC – Concetti


principali
Giovanni Grasso

This%work%is%licensed%under%a%Crea2ve%Commons%A6ribu2on8Noncommercial8Share%Alike%3.0%United%States%
See%h6p://crea2vecommons.org/licenses/by8nc8sa/3.0/us/%for%details%
Gestione della persistenza
Le nostre applicazioni sono sviluppate con linguaggi OO
(lo stesso vale anche per i moderni sistemi informativi)
mentre la persistenza dei dati è affidata ai DBMS relazionali
OO: scalabilità e riusabilità
RDBMS: affidabilità, efficienza, efficacia
Esistono differenze significative tra queste due tecnologie:
differenze tecnologiche
differenze culturali
Si parla di conflitto di impedenza

2
Conflitto di impedenza (1/4)
Definito anche come “disaccoppiamento di impedenza”
(o impedance mismatch) tra base di dati e linguaggio
linguaggi: operazioni su singole variabili o oggetti
SQL: operazioni su relazioni (insiemi di ennuple)
Differenze di accesso ai dati e correlazione:
linguaggio: dipende dal paradigma e dai tipi disponibili;
ad esempio scansione di liste o “navigazione” tra oggetti
SQL: join (ottimizzabile)

3
Conflitto di impedenza (2/4)
Differenze sui tipi di dato primitivi:
linguaggi: numeri, stringhe, booleani
SQL: CHAR, VARCHAR, DATE, ...
Differenze sui costruttori di tipo:
linguaggio: dipende dal paradigma
SQL: relazioni e ennuple

OID (trasparenti al programmatore) vs. chiavi primarie


(visibili e manipolabili)
Riferimenti vs. chiavi esterne
Ereditarietà (senza controparte nel mondo relazionale)

4
Conflitto di impedenza (3/4)
In generale, i dati risiedono nel database (DB) mentre la
logica applicativa viene implementata da oggetti
Dal modello concettuale si può derivare (una prima
versione di)
classi che implementano la logica applicativa (diciamo
che queste classi implementano il modello di dominio, o
più semplicemente il modello)
classi "facade" che offrono le operazioni
(metodi) dell’applicazione
schema relazionale per memorizzare i dati
questa attività può essere fatta anche successivamente
in alcuni casi (sistemi legacy) il database potrebbe esistere già e
potrebbe non essere modificabile
• quindi tutte le attività di progetto sono condizionate da questo vincolo

5
Metodologie per la gestione della persistenza
In generale possiamo pensare che le operazioni (metodi)
degli oggetti facade
caricano tuple dalla base di dati e le usano per creare gli
oggetti del modello
effettuano le operazioni della logica applicativa
(es. evadi ordine)
salvano in maniera persistente gli oggetti del modello
nella base di dati
L'interazione con il DB è una operazione critica: esistono
diverse metodologie (di complessità crescente) per
realizzare questa interazione
forza bruta
pattern DAO
framework ORM (..alla magistrale..)

7
Forza bruta
È la tecnica più semplice per gestire la persistenza
forte accoppiamento con la sorgente dati
Consiste nello scrivere dentro le classi del modello un
insieme di metodi che implementano le operazioni CRUD
Operazioni CRUD
Create: inserimento di una tupla (che rappresenta un oggetto) nel
database (INSERT)
Retrieve: ricerca di una tupla secondo un qualche criterio di ricerca
(SELECT)
Update: aggiornamento di una tupla nel database (UPDATE)
Delete: eliminazione di una tupla nel database (DELETE)
Ci possono essere diverse operazioni di Retrieve
diversi criteri: “per chiave” vs. “per attributo” (es. ricerca cliente per
codice, per nome, etc.)
diversi risultati (un oggetto, una collezione di oggetti) a seconda del
criterio

8
Forza bruta in sintesi (1/2)
Per ogni classe MyC che rappresenta una entità del
dominio, si definiscono:
un metodo doRetrieveByKey(X key) che
restituisce un oggetto istanza di MyC i cui dati sono letti
dal database
• tipicamente da una tabella che è stata derivata dalla stessa
classe del modello di dominio che ha dato origine a MyC
recupera i dati per chiave

un metodo saveOrUpdate(…) che salva i dati


dell'oggetto corrente nel database
il metodo esegue una istruzione SQL update o insert a
seconda che l'oggetto corrente esista già o meno nel
database

9
Forza bruta in sintesi (2/2)
uno o più metodi doRetrieveByCond(…) che
restituiscono una collezione di oggetti istanza della
classe MyC che soddisfano una qualche condizione
(basata sui parametri del metodo)

un metodo doDelete(…) che cancella dal database i


dati dell'oggetto corrente

10
Il pattern Data Access Object
La soluzione Forza bruta non è particolarmente
conveniente
L'accoppiamento tra la sorgente dati e le classi del
modello è molto forte
Le classi del modello sono poco coese (responsabilità
eterogenee)
Molto difficile iniziare a sviluppare senza preoccuparsi di
tanti (troppi !) dettagli e senza un ambiente operativo
complesso (java + dbms)
Una soluzione "naturale" è quella di affidare le
responsabilità di interagire con il database ad opportune
classi
Questo è l'approccio suggerito dal pattern
Data Access Object (DAO)

11
Il pattern DAO
Un pattern è un “modo di fare le cose” che si è dimostrato
efficace e che per questo si tende ad applicare di nuovo
Il pattern DAO è uno degli standard “J2EE design patterns”;
rappresenta un possibile modo di separare:
logica di business (es: Servlet, JSP, …)
logica di persistenza (es: r/w su DB, …)
Infatti, i componenti della logica di business non dovrebbero
mai contenere codice che accede direttamente al database!
scarsa manutenibilità
sovrapposizione di responsabilità
Solo gli oggetti previsti dal pattern DAO
hanno il permesso di “vedere” il DB
espongono metodi di accesso per tutti gli altri componenti

12
DAO: Caratteristiche principali (1/2)
I valori scambiati tra DB e il resto dell’applicazione sono
racchiusi in oggetti detti Data Transfer Object (DTO):
campi privati per contenere i dati da leggere/scrivere su
db
metodi getter e setter per accedere dall’esterno a tali
campi
metodi di utilità (confronto, stampa, …)
Le operazioni che coinvolgono tali oggetti sono raggruppati
in interfacce che definiscono i Data Access Object (DAO)
disponibili:
metodi CRUD
altri metodi

13
DAO: Caratteristiche principali (2/2)
Le implementazioni di tali interfacce (spesso indicate come
oggetti DAO, per brevità) permettono l’effettivo accesso al
database
diverse implementazione possono permettere (e rendere
uniforme) l'accesso a DB di diversi vendor
anche se si fa uso di un solo database, tale separazione
migliora comunque la divisione delle responsabilità tra le
parti dell’applicazione
diventa facile migrare l’applicazione su DB diversi
Gli oggetti DAO non sono istanziati direttamente dai
componenti, ma:
possono essere ottenuti attraverso metodi factory

14
Pattern DAO con classi Factory
Una unica factory (anche astratta)
fornisce specifiche per le factory concrete
espone un metodo creazionale parametrico per ottenere
factory concrete
Una factory concreta per ogni tipo di DB supportato
permette di ottenere oggetti DAO appropriati al
corrispondente tipo di DB
può gestire aspetti quali ottenimento della connessione,
autenticazione, ...
Un oggetto DTO per ogni tipo di entità che si vuole
rappresentare
Una interfaccia DAO per ogni oggetto DTO
Una implementazione dell'interfaccia DAO di un DTO per
ciascun DB supportato
15
Pattern DAO con classi Factory

16
DAO in pratica
Per ogni classe Entità del modello di dominio creiamo
(in un opportuno package) una interfaccia DAOEntità
con (le signature dei) metodi che realizzano le operazioni
CRUD
Queste interface sono implementate da classi
(organizzate in opportuni package)
questa scomposizione permette maggiore flessibilità

20
Esempio (in UML)

Prodotto
1 Ordine
CodiceProdotto * RigaOrdine * 1 CodiceOrdine
Nome Quantità
Data
Descrizione NumeroLinea
stato
Costo

1
Cliente
CodiceCliente
Nome
Indirizzo

21
Esempio: ProdottoDAO
public interface ProdottoDAO {

public List<Prodotto> doRetrieveAll()


throws PersistenceException;

public Prodotto doRetriveByKey(String codice)


throws PersistenceException;

public void saveOrUpdate(Prodotto prodotto)


throws PersistenceException;

public void delete(Prodotto prodotto)


throws PersistenceException;

// eventuali altri doRetriveBy...


}

22
Esempio: ProdottoDAOImpl
public class ProdottoDAOImpl implements ProdottoDAO {

public List<Prodotto> doRetrieveAll()


throws PersistenceException {
List<Prodotto> prodotti;


try {
…//uses JDBC to connect to DB
} catch (SQLException sqle) {
throw new PersistenceException(sqle);
} finally {

}
return prodotti;
}


}

23
Interazione Facade-DAO: Esempio

ProdottoDAO

Facade ProdottoMySqlDAO MySQL

Prodotto

24
Caso di studio
package modello;

import java.util.*;
import persistenza.*;

public class FacadeImpl implements Facade {


private ClienteDAO clienteDao;
….

public Facade() {
clienteDao = new ClienteDAOImpl();
}

public void inserisciClienteNellaAnagrafica(Cliente cliente) throws ClienteException {


try {
clienteDao.saveOrUpdate(cliente);
} catch (PersistenceException e) {
throw new ClienteException("Exception in ClientiFacade while creating cliente.");
}
}

public List<Cliente> anagraficaClienti(){


try {
return clienteDao.doRetrieveClienteAll();
} catch (PersistenceException e) {
throw new ClienteException("Exception in ClientiFacade while retrieving cliente.", e);
}
}

public Cliente trovaClientePerCodice(String codiceCliente){


try {
return clienteDao.doRetrieveClienteByCodice(codiceCliente);
} catch (PersistenceException e) {
throw new ClienteException("Exception in ClientiFacade while retrieving cliente"+ codiceCliente +, e);
}
}

}

ORM e Hibernate 27
JDBC - Concetti Fondamentali
•  Riferimenti
–  http://java.sun.com/javase/6/docs/technotes/guides/jdbc/
•  Introduzione
–  Architettura
–  Tipi di driver
•  Un Esempio
–  Driver e Driver Manager
–  Connection
–  PreparedStatement
–  ResultSet
–  SQLException
Introduzione
•  Da Java Tutorial
–  The JDBC API is a Java API that can access any kind
of tabular data, especially data stored in a Relational
Database
–  JDBC helps you to write java applications that
manage these three programming activities:
•  Connect to a data source, like a database
•  Send queries and update statements to the database
•  Retrieve and process the results received from the database
in answer to your query
•  Dalla documentazione JavaDoc
–  java.sql: classi fondamentali
–  javax.sql: estensioni
Introduzione
•  Una applicazione che accede (in lettura e/
o scrittura) ad una sorgente di dati (nel
nostro caso un database relazionale) ha
bisogno di fare le seguenti operazioni:
–  1. aprire una connessione alla sorgente dati
–  2. inviare attraverso la connessione istruzioni
(di interrogazione e aggiornamento) alla
sorgente dati
–  3. processare i risultati ricevuti dalla sorgente
dati in risposta alle istruzioni inviate
Introduzione
•  Le nostre sorgenti dati sono database
relazionali, gestiti da un DBMS
•  Ogni DBMS espone una API
•  Le applicazioni Java interagiscono con le
API del DBMS attraverso un driver
Architettura

Applicazione%Java% utilizza
le interfacce
java.sql
Driver%Manager% e javax.sql

implementano
Driver% Driver% Driver%
java.sql e,
in alcuni casi
IBM javax.sql
Postgres% Oracle
Db2
Data-source
Tipi di Driver
•  Type 1 - drivers that implement the JDBC API as a mapping to
another data access API, such as ODBC. Drivers of this type are
generally dependent on a native library, which limits their portability.
The JDBC-ODBC Bridge driver is an example of a Type 1 driver.
•  Type 2 - drivers that are written partly in the Java programming
language and partly in native code. These drivers use a native client
library specific to the data source to which they connect. Again,
because of the native code, their portability is limited.
•  Type 3 - drivers that use a pure Java client and communicate with a
middleware server using a database-independent protocol. The
middleware server then communicates the client s requests to the
data source.
•  Type 4 - drivers that are pure Java and implement the network
protocol for a specific data source. The client connects directly to the
data source.
Confronto tra driver
•  Type 1 (Ponte JDBC-ODBC)
–  prestazioni scadenti
–  non indipendente dalla piattaforma
–  fornito a corredo di SDK
•  Type 2
–  migliori prestazioni
–  non indipendente dalla piattaforma
•  Type 3
–  client indipendente dalla piattaforma
–  servizi avanzati (caching)
–  architettura complessa
•  Type 4
–  indipendente dalla piattaforma
–  buone prestazioni
–  scaricabile dinamicamente
•  Per approfondimenti:
–  http://java.sun.com/products/jdbc/driverdesc.html
Esempio
•  Obiettivo: esemplificare l API JDBC. Quindi:
–  scriviamo codice che ha unicamente lo scopo di
evidenziare l'uso della API JDBC
–  non ci preoccupiamo della qualità del codice Java
•  Operazioni
–  inserimento di una tupla nel db
–  cancellazione di una tupla
–  ricerca di un tupla per chiave primaria
–  ricerca di un insieme di tuple (per qualche
proprietà)
Esempio
•  Consideriamo la classe Studente:

import java.util.Date;
public class Studente {
private String matricola;
private String nome;
private String cognome;
private Date dataNascita;

public Studente(){}
public String getNome() {
return this.nome;
}
public void setNome(String nome) {
this.nome = nome;
}
// seguono tutti gli altri metodi getter e setter
}
Esempio
e il database university:
CREATE DATABASE university;

CREATE TABLE studente


(
matricola character(8);
nome character varying(64) NOT NULL,
cognome character varying(64) NOT NULL,
datanascita date NOT NULL,
CONSTRAINT pk_studente PRIMARY KEY (matricola)
)
Ambiente
•  DBMS
–  a scelta
(PostgreSQL, MySQL, …)
–  consigliato: PostgreSQL
•  Driver JDBC per il DBMS scelto
–  con postgresql
–  http://jdbc.postgresql.org/
–  con altri DBMS, scaricare un driver appropriato
•  Ambiente Java standard
•  Nota bene: il .jar del driver deve essere nel
BUILDPATH
Un Esempio
•  Nel seguito, con riferimento ad nostro
studio di caso, descriveremo le seguenti
operazioni
–  Operazione n.1: Caricare il driver
–  Operazione n.2: Aprire una connessione
–  Operazione n.3: Definire l istruzione SQL
–  Operazione n.4: Gestire il risultato
–  Operazione n.5: Gestire le eccezioni
Le classi fondamentali di JDBC
•  Package java.sql (va importato)

•  Classe DriverManager
•  Interfaccia Driver
•  Interfaccia Connection
•  Interfaccia Statement
•  Interfaccia ResultSet
•  Eccezione SQLException
Driver
•  Rappresenta%il%punto%di%partenza%per%o6enere%
una%connessione%a%un%DBMS%%
•  ︎I%produ6ori%di%driver%JDBC%implementano%
l”interfaccia%Driver%(mediante%opportuna%
classe)%affinché%possa%funzionare%con%un%2po%
par2colare%di%DBMS%%
•  ︎ La%classe%che%implementa%Driver&può%essere%
considerata%la%“factory”%per%altri%oggeV%JDBC%
–  ad%esempio,%oggeV%di%2po%Connec2on%%
Operazione n.1
Caricare il Driver
•  Creare un oggetto della classe Driver
Driver d = new org.postgresql.Driver();

•  Registrare il driver sul DriverManager


void DriverManager.registerDriver(Driver d);

•  A questo punto il driver è disponibile


•  Le operazioni precedenti sono equivalenti
alla istruzione:
Class.forName("org.postgresql.Driver");
Interfaccia Connection
•  Un%ogge6o%di%2po%Connec2on%rappresenta%
una%connessione%aVva%con%il%DB%%
•  ︎Il%metodo%getConnec-on&di%DriverManager,%
se%non%fallisce,%res2tuisce%un%ogge6o%di%2po%
Connec-on&%
•  L”interfaccia%me6e%a%disposizione%una%serie%di%
︎L”interfaccia%me6e%a%disposizione%una%serie%di%
metodi%per%le%operazioni,%tra%le%quali:%
–  Preparazione%di%query%SQL%da%inviare%tramite%
oggeV%
•  Statement,&PreparedStatement,&CallableStatement&%
Operazione n.2
Ottenere una Connessione
•  Ottenere una connessione dal
DriverManager
–  per farlo è necessario specificare:
•  host, dbms, db
•  utente e password
•  URI della connessione
–  specifica server, porta e database
•  Sintassi
jdbc:<sottoprotocollo>:<parametri>
Operazione n.2
Ottenere una Connessione

•  URI per PostgreSQL


jdbc:postgresql:<baseDati>
jdbc:postgresql://<host>/<baseDati>
jdbc:postgresql://<host>:<porta>/<baseDati>
•  Esempi
jdbc:postgresql:university
jdbc:postgresql://127.0.0.1/university
jdbc:postgresql://193.204.133.14:5432/university
jdbc:odbc:university
Operazione n.2
Ottenere una Connessione
•  Creazione della connessione
Connection DriverManager.getConnection(
String uri,
String utente,
String password);
•  Esempio
Connection connection;
connection = DriverManager.getConnection(
"jdbc:postgresql:university" ,
”user",
”psw" );
•  Attenzione: ottenere una connessione è
un operazione costosa
–  creare troppe connessioni comporta problemi di
prestazioni
–  in seguito vedremo tecniche per superare questo problema
Nell'esempio
•  Confiniamo in una classe, DataSource, le
operazioni necessarie per ottenere la
connessione
–  il suo compito è servire connessioni alle altre
classi che ne hanno bisogno
–  metodo Connection getConnection() che
restituisce una nuova connessione ad ogni
richiesta
•  E' una soluzione artigianale usata solo a fini
didattici
•  Connection pooling e’ la soluzione ottima
(vedi Tomcat ed altri container)
La classe DataSource
import java.sql.*;

public class DataSource {


private String dbURI = "jdbc:postgresql://localhost/university";
private String userName = ”user";
private String password = ”psw";

public Connection getConnection() throws PersistenceException {


Connection connection = null;
try {
Class.forName("org.postgresql.Driver");
connection = DriverManager.getConnection(dbURI,userName, password);
} catch (ClassNotFoundException e) {
throw new PersistenceException(e.getMessage());
} catch(SQLException e) {
throw new PersistenceException(e.getMessage());
}
return connection;
}
}
PersistenceException è una classe eccezione (che estende Exception) definita da noi
La classe
PersistenceException%
public class PersistenceException extends RuntimeException {
private static final long serialVersionUID = 1L;

public PersistenceException(String message) {


super(message);
}

public PersistenceException(Throwable cause) {


super(cause);
}

public PersistenceException(String message, Throwable cause) {


super(message, cause);
}
}
Interfaccia Statement
•  Gli%oggeV%di%2po%Statement&possono%essere%
usa2%per%query%SQL%semplici%%
–  query%che%non%fanno%uso%di%parametri%%
•  La%query%può%essere%di%modifica%UPDATE,%
INSERT,%CREATE%oppure%di%selezione%SELECT%%
•  ︎Per%query%di%2po%SELECT%il%risultato%è%inserito%
in%un%ogge6o%ResultSet&%
•  ︎Un%ogge6o%Statement&può%essere%creato%con%
il%metodo%createStatement&di%Connec-on&%
Interfaccia PreparedStatement
•  Gli%oggeV%di%2po%PreparedStatement&possono%
essere%usa2%per%creare%query%SQL%parametriche%
(parametri%di%input)%e%precompilate(“prepared”)%%
•  ︎Il%valore%di%ciascun%parametro%non%è%specificato%
nel%momento%in%cui%lo%statement%SQL%è%definito,%
ma%rimpiazzato%dal%cara6ere%“?‟%%
•  ︎È%più%efficiente%perché%una%volta%che%la%query%è%
preparata,%sarà%pre8compilata%per%usi%futuri%%
•  ︎Un%ogge6o%PreparedStatement&può%essere%
creato%con%il%metodo%prepareStatement&di%
Connec-on&%
Interfaccia CallableStatement
•  Gli%oggeV%di%2po%CallableStatement&possono%
essere%usa2%per%definire%query%parametriche%
con%parametri%di%2po%IN,%OUT%e%INOUT%%
•  Perme6ono%di%eseguire%una%invocazione%a%una%
︎Perme6ono%di%eseguire%una%invocazione%a%una%
stored%procedure%memorizzata%sul%server%DB%%
•  ︎Un%ogge6o%CallableStatement&può%essere%
creato%con%il%metodo%prepareCall&di%
Connec-on&%
Operazione n.3
Istruzione SQL
•  Vediamo ora il codice JDBC che esegue
istruzioni SQL per:
–  salvare (rendere persistenti) oggetti nel db
–  cancellare oggetti dal db
–  trovare oggetti dal db
•  Questo e’ codice che appartiene alle classi
DAO
Operazione n.3
Istruzione SQL
•  Usiamo PreparedStatement
–  creato dall'oggetto Connection invocando il metodo:
•  La stringa insert_stm è una istruzione SQL
parametrica: i parametri sono indicati con il simbolo ?
•  Esempio 1
String insert_stm = "insert into studente(nome,
cognome, datanascita, matricola) values (?,?,?,?)";
PreparedStatement statement =
connection.prepareStatement(insert_stm );
•  Esempio 2
String delete_stm = "delete from studente where
matricola=?";
PreparedStatement statement =
connection.prepareStatement(delete_stm );
Operazione n.3
Istruzione SQL
•  I parametri sono assegnati mediante opportuni metodi della classe
che implementa PreparedStatement
–  metodi setXXX (<numPar>, <valore>)
–  un metodo per ogni tipo, il primo argomento corrisponde all'indice del
paramentro nella query, il secondo al valore da assegnare al parametro

•  Esempio 1 (cont.)
PreparedStatement statement;
String insert_stm = "insert into studente(nome, cognome,
datanascita, matricola) values (?, ?,?,?)";

statement = connection.prepareStatement(insert_stm );
statement.setString(1, studente.getNome());
statement.setString(2, studente.getCognome());

long secs = studente.getDataNascita().getTime());


statement.setDate(3, new java.sql.Date(secs));

statement.setString(4, studente.getMatricola());
Operazione n.3
Istruzione SQL
•  Una volta assegnati i valori ai parametri,
l'istruzione può eseguita
•  Distinguiamo due tipi di operazioni:
–  Modifiche (insert, update, delete)
•  modificano lo stato del database
–  interrogazioni (select)
•  non modificano lo stato del database
•  ritornano una sequenza di tuple
Operazione n.3
Istruzione SQL
•  Aggiornamenti (insert, delete, update)
–  Si invoca il metodo executeUpdate() sull'oggetto
PrepareStatement
•  Esempio 1 (cont.)
PreparedStatement statement;
String insert_stm = "insert into studente(nome, cognome,
dataNascita, matricola) values (?,?,?,?)";
statement = connection.prepareStatement(insert_stm );
statement.setString(1, studente.getNome());
statement.setString(2, studente.getCognome());
long secs = studente.getDataNascita().getTime());
statement.setDate(3, new java.sql.Date(secs));
statement.setString(4, studente.getMatricola());

statement.executeUpdate();
Operazione n.3
Istruzione SQL
•  Interrogazioni (select)
–  vengono eseguite invocando il metodo
executeQuery()
–  che ritorna il risultato in un oggetto ResultSet
•  Esempio 2
PreparedStatement statement;
String query = "select * from studente
where matricola=?";
statement = connection.prepareStatement(query);
statement.setString(1, matricola);
ResultSet result = statement.executeQuery();
Oggetto ResultSet
•  Un oggetto della classe ResultSet rappresenta la
collezione di ennuple restituita da una query SQL
(istruzione SELECT)
•  Per gestire il risultato offre vari metodi:
–  metodo boolean next() per scorrere le ennuple
(analogo ad un iteratore)
–  metodi getXXX(String nome-colonna) per o
getXXX(int posizione-colonna) per acquisire i
valori degli attributi della tupla
Es.: int getInt(…);
Es.: String getString(…);
•  I valori NULL sono convertiti in null, 0, oppure
false, dipendentemente dal tipo di metodo getXXX
Operazione n.4
Gestire il risultato di una query
•  Il metodo next()
–  Sposta il cursore dalla riga corrente alla successiva. La prima
chiamata a next() ottiene la prima riga, la seconda chiamata ottiene la
seconda riga e cosi via.
–  Returns: true se la nuova riga corrente e’ valida, falso se non vi sono
piu’ righe
–  Se non vi sono piu’ righe, il cursore e’ posizionato DOPO l’ultima riga,
ed ogni invocazione di qualsiasi metodo di ResultSet che richiede
una riga corrente, sollevera’ una SQLException
public List<Studente> findAll() {
Connection connection = this.dataSource.getConnection();
List<Studente> studenti = new LinkedList<>();
try {
Studente studente;
PreparedStatement statement;
String query = "select * from studente";
statement = connection.prepareStatement(query);
ResultSet result = statement.executeQuery();
while (result.next()) {
studente = new Studente();
studente.setMatricola(result.getString("matricola"));
studente.setNome(result.getString("nome"));
studente.setCognome(result.getString("cognome"));
long secs = result.getDate("datanascita").getTime();
studente.setDataNascita(new java.util.Date(secs));
studenti.add(studente);
}
} catch (SQLException e) {
throw new PersistenceException(e.getMessage());
} finally {
try {
connection.close();
} catch (SQLException e) {
throw new PersistenceException(e.getMessage());
}
}
return studenti;
}
Operazione n.4
Gestire il risultato di una query%

List<Studente> studenti = new LinkedList<>;


Studente studente;
PreparedStatement statement;
String query = "select * from studente";
statement = connection.prepareStatement(query);
ResultSet result = statement.executeQuery();
while (result.next()) {
studente = new Studente();
studente.setMatricola(result.getString("matricola"));
studente.setNome(result.getString("nome"));
studente.setCognome(result.getString("cognome"));
long secs = result.getDate("datanascita").getTime();
studente.setDataNascita(new java.util.Date(secs));
studenti.add(studente);
}%
Controllo sul tipo di Dato
•  l%dialogo%tra%una%applicazione%Java%e%i%DB%so6ostan2%
richiede%che%i%2pi%di%dato%SQL%siano%mappa2%in%
corrisponden2%2pi%di%dato%Java%e%viceversa.%%
•  La%conversione%(SQL2Java)%riguarda%tre%categorie:%%
–  Tipi%SQL%che%hanno%direV%equivalen2%in%Java%%
•  (es.%il%2po%SQL%INTEGER&%!%Java%int)%%
–  Tipi%SQL%che%possono%essere%conver22%negli%equivalen2%
2pi%Java%%
•  (es.%i%2pi%SQL%CHAR%e%!%Java%String)%%
–  Tipi%SQL%che%sono%unici%(una%minoranza)%e%che%necessitano%
la%creazione%di%uno%speciale%ogge6o%Java%%
•  (es.%il%2po%SQL%DATE%si%converte%nell’ogge6o%Date%definito%
dall’omonima%classe%Java)%%
Nota sul tipo Data

•  JDBC usa java.sql.Date, mentre la classe Studente usa


java.util.Date

•  Le istruzioni
long secs = student.getBirthDate().getTime());
statement.setDate(4, new java.sql.Date(secs));

servono a "convertire" una data da una rappresentazione


all'altra

•  Per i dettagli vedi la documentazione


Operazione n.5
gestire le eccezioni
•  Tutti i metodi delle classi dell'API JDBC viste fino
ad ora lanciano una eccezione SQLException
•  Connessione, statement, resultSet devono
essere sempre "chiusi" dopo essere stati usati
–  l'operazione di chiusura corrisponde al rilascio di risorse
–  Effettuiamo queste operazioni nella clausola finally
–  abbiamo così la garanzia che vengono comunque
effettuate
•  Se vengono sollevate eccezioni, le rilanciamo
con una eccezione di livello logico opportuno
(nel nostro caso PersistenceException)
Aspetti Metodologici
Risolvere il conflitto di impedenza
•  Gestione delle associazioni
– Problemi
– Lazy Load
•  Gestione dei riferimenti
–  Chiavi naturali e chiavi surrogate
–  Generazione chiavi surrogate
•  Operazioni in cascata
–  Insert, Update, Delete
Risolvere il conflitto di impedenza

•  Analizziamo%ora%i%principali%problemi%dovu2%al%
confli6o%di%impedenza%e%ne%presen2amo%le%
soluzioni%%
•  A%scopo%didaVco%lavoriamo%su%DAO,%ma%le%
considerazioni%che%facciamo%seguendo%questa%
soluzione%hanno%validità%generale!!%%
•  Le%problema2che%rela2ve%al%confli6o%di%
impedenza%sono%alla%base%della%metodologia%
Object%Rela2onal%Mapping%(ORM)%che%esula%dagli%
obieVvi%del%corso%
Risolvere il conflitto di impedenza

•  Nel%modello%OO,%le%relazioni%tra%oggeV%sono%
realizzate%con%riferimen2,%mentre%nel%modello%
relazionale%sono%realizzate%con%i%valori%%
•  Vediamo%problemi%e%soluzioni%rela2vamente%a:%
–  Retrieve:%dal%DB%agli%oggeV%(ricostruzione%degli%
oggeV%a%par2re%da%da2%persisten2)%%
•  Create:%dagli%oggeV%al%DB%
–  (memorizzazione%persistente%degli%oggeV%nel%DB)%%
Esempio%di%riferimento%

Studente&
Gruppo&
*% 8matricola:%String%
8id:%Long% 8nome:%String%
8Nome:%String% 8cognome:%String%
8dataNascita:%Date%

gruppo& studente&
id% bigint% nullable=false% matricola% character(8)% nullable=false%
PK% PK%
nome% varchar(255)% nullable=false% nome% varchar(255)% nullable=false%
cognme% varchar(255)% nullable=false%
data_nascita% date% nullable=true%
gruppo_id% bigint% FK%gruppo.id%
Esempio%di%riferimento%
gruppo& studente&
id% bigint% nullable=false% matricola% character(8)% nullable=false%
PK% PK%
nome% varchar(255)% nullable=false% nome% varchar(255)% nullable=false%
cognme% varchar(255)% nullable=false%
data_nascita% date% nullable=true%
gruppo_id% bigint% FK%gruppo.id%

gruppo% studente%
ID& NOME& matricola& nome& cognome& data_nascita& gruppo_id&

1% Le%Mele% 00000001% Roberto% Bianchi% 20/05(1997% 1%

2% Microgeeks% 00000002% Gilia% Rossi% 12/02(1997% 1%

…% …% …% …% …% …%
Esempio%di%riferimento%
public class Gruppo {
private Long id;
private String nome;
private Set<Studente> studenti;

public Gruppo(){
this.studenti = new HashSet<>();
}

// getter e setter

public void addStudente(Studente studente){


this.getStudenti().add(studente);
}

public void removeStudente(Studente studente){


this.getStudenti().remove(studente);
}
}%
Esempio%di%riferimento%
public class Studente {
private String matricola;
private String nome;
private String cognome;
private Date dataNascita;

public Studente(){}

// getter e setter
// hashCode e equals

%
Ricostruzione degli oggetti dal db
•  Consideriamo%la%ricostruzione%di%un%ogge6o%Gruppo,%
ad%esempio%per%definire%il%metodo%%
Gruppo findByPrimaryKey(Long id)%
–  nella%classe%Gruppo:%%
•  una%collezione%di%riferimen2%a%oggeV%Studente;%%
–  nel%database:%%
•  tabella%gruppo
•  tabella%studente (foreign%key%con%gruppo)%

•  Per%ricostruire%un%ogge6o%Gruppo "completo"%devo%
ricostruire%la%collezione%di%oggeV%Studente (e%
anche%ques2%oggeV%devono%essere%"comple2"?!)
Ricostruzione degli oggetti dal db
•  Come ricostruiamo l'oggetto Gruppo "completo"?
•  Se voglio ricostruire la collezione di riferimenti agli
studenti
–  eseguo un join tra le tabelle gruppo e studente
–  itero sul ResultSet e costruisco i vari oggetti Gruppo e
Studente (aggiungendone i riferimenti alla collezione)
•  NB: che join usiamo?
–  è possibile che nella tabella gruppo ci siano tuple per le
quali non ci sono righe nella tabella studente?
–  se la risposta è SI, allora devo usare un outer join
Esempio%
•  Scriviamo%la%classe%DAO%per%Gruppo%
•  Concentriamoci%sul%metodo%che%implementa%
l'operazione%CRUD%di%RETRIEVE:%%
–  Gruppo%findByPrimaryKey(Log%id)%
Esempio%con%Join%
public Gruppo findByPrimaryKeyJoin(Long id) {
Connection connection = this.dataSource.getConnection();
Gruppo gruppo = null;
try {
PreparedStatement statement;
String query = "SELECT g.id AS g_id, g.nome AS g_nome, "

+ "s.matricola AS matricola, s.nome AS nome, "
+ "s.cognome AS cognome, "

+ "s.datanascita AS datanascita "
+ "FROM gruppo g LEFT OUTER JOIN " 

+ "studente s ON g.id=s.gruppo_id "
+ "WHERE g.id = ?";
statement = connection.prepareStatement(query);
statement.setLong(1, id);
logger.debug(statement);
ResultSet result = statement.executeQuery();
Esempio%con%Join%(cont.)%
boolean primaRiga = true;
while (result.next()) {
if (primaRiga) {
gruppo = new Gruppo();
gruppo.setId(result.getLong("g_id"));
gruppo.setNome(result.getString("g_nome"));
primaRiga = false;
}
if(result.getString("matricola")!=null){
Studente studente = new Studente();
studente.setMatricola(result.getString("matricola"));
studente.setNome(result.getString("nome"));
studente.setCognome(result.getString("cognome"));
long secs = result.getDate("datanascita").getTime();
studente.setDataNascita(new java.util.Date(secs));
gruppo.addStudente(studente);
}
}
} catch (SQLException e) { ///…
Esempio%con%Join:%note%
•  Query%SQL:%
SELECT g.id AS g_id, g.nome AS g_nome, 

s.matricola AS matricola, s.nome AS nome,

s.cognome AS cognome, 

s.datanascita AS datanascita 

FROM gruppo g LEFT OUTER JOIN studente s 

ON g.id=s.gruppo_id 

WHERE g.id = ?
•  Perchè%LEFT%OUTER%JOIN?%
–  gruppi%senza%studen2%non%verrebbero%trova2%
•  Perchè%gli%alias%nella%target%list?%
–  JDBC%fa%riferimento%ai%nomi%delle%colonne%
Problemi%soluzione%Join%
•  La%soluzione%con%il%join%ha%un%(potenzialmente%
grosso)%problema%di%prestazioni%
•  Non%è%scontato%che%le%operazioni%che%devo%fare%
sui%da2%in%memoria%richiedano%i%da2%degli%oggeV%
collega2%(ad%esempio%mi%servono%solo%I%nomi%dei%
gruppi)%%
•  Per%ovviare%a%questo%problema,%facciamo%in%modo%
che%gli%oggeV%della%collezione%siano%carica2%solo%
se%necessario,%cioè%solo%se%viene%invocato%un%%
metodo%che%richiede%l'accesso%alla%collezioni%
–  nel%nostro%esempio%il%metodo%getStuden2()%
Ricostruzione degli oggetti dal db
•  Ma che cosa è un "oggetto completo"?
–  con un modello di dominio complesso questa
strategia può portarci a caricare una rete
molto vasta di oggetti
(esempio: Professore"Corso"Gruppo!Studente!…)
•  Fino a quando devo costruire oggetti
"inseguendo i riferimenti"?
–  e quindi calcolando costosi join?
Ricostruzione degli oggetti dal db
•  Possiamo dare una risposta a queste domande
analizzando i casi d'uso e il modello del dominio
•  In particolare l'inseguimento dei riferimenti tra
oggetti è guidato dalle operazioni offerte dal sistema
•  Nel progettare le classi, i riferimenti vanno messi
considerando la direzione in cui le corrispondenti
associazioni sono navigate dalle operazioni del
sistema
•  questo aspetto non ha rilevanza nella progettazione del
database, perché i riferimenti sono implementati tramite
valori e l'operatore di join non è direzionale
Ricostruzione degli oggetti dal db
•  E quando non ha senso costruire un oggetto
completo, come mi comporto?
•  (i metodi che richiedono l'inseguimento di un
riferimento, se invocati devono ritornare l'oggetto
corretto!)
•  In questo caso ricorriamo ad una tecnica, detta
Caricamento Pigro, implementata tramite
l'applicazione del Pattern Proxy
Lazy Load (caricamento pigro)
•  Consideriamo la classe Gruppo
•  Se non è ragionevole pensare che per caricare i
dati di un gruppo si debba ricostruire l'intera rete
di oggetti collegati dai riferimenti nelle collezioni,
possiamo pensare che la collezione di studenti
associati al gruppo possa essere caricata solo
quando richiesta:
–  quando invochiamo il metodo getStudenti() su un
oggetto Gruppo
•  Questa strategia si chiama Lazy Load
(caricamento pigro)
Lazy Load (caricamento pigro)
•  In pratica quello che vogliamo fare è caricare i dati solo quando questi
sono effettivamente richiesti
•  Vediamo come dovrebbe agire il metodo getStudenti () della
classe Gruppo seguendo questa strategia
–  quando invocato il metodo getStudenti() esegue la query per costruire gli
oggetti Studente associati al gruppo, aggiunge i riferimenti a questi oggetti
nella collezione dell'oggetto Gruppo, e restituisce la collezione
•  E' importante osservare che se il metodo non viene mai invocato, il
costo di caricare i dati relativi agli ordini non viene mai pagato
•  Non possiamo però implementare questa strategia nella classe
Gruppo: metteremmo codice di gestione della persistenza in una
classe del dominio
•  Per implementare bene la strategia Lazy Load senza ridurre la coesione
nella classe del modello è utile fare riferimento al pattern Proxy
Il pattern proxy: structure

Subject
Client
request()

Proxy RealSubject

request() request()
Pattern Proxy:
Participants and Responsibilities

Client Proxy RealSubject Resource

request()
request()
Lazy Load e classi proxy
•  I DAO non restituiscolo un oggetto
completo ma un proxy
–  Il proxy ha il compito di gestire il caricamento
degli oggetti collegati
–  Il proxy deve contenere tutte le informazioni
necessarie per effettuare il caricamento lazy
–  Gli oggetti collegati vengono caricati solo su
richiesta
–  Il proxy nasconde l'accesso al database
Lazy Load: dettagli
•  Quando chiediamo ad un DAO di recuperare un
oggetto (o una collezioni di oggetti), questi esegue
una query sul DB, e con i dati recuperati istanzia e
restituisce l'oggetto richiesto
•  L'oggetto restituito non è però del tipo richiesto,
ma è un proxy, il cui tipo estende il quello richiesto
e riscrive i metodi che devono implementare le
operazioni onerose
–  nell'esempio, l'operazione onerosa consiste nel
metodo getStudenti()
Lazy Load
public%class%GruppoDaoJDBC%{%
//%… %%
public&Gruppo&findByPrimaryKey(Long&id)&{&
% %Connec2on%connec2on%=%this.dataSource.getConnec-on();&
% %Gruppo%gruppo%=%null;&
% %try&{&
% % %PreparedStatement%statement;%
% % %String%query%=%"select%*%from%gruppo%where%id%=%?";%
% % %statement%=%connec2on.prepareStatement(query);%
% % %statement.setLong(1,%id);%
% % %ResultSet%result%=%statement.executeQuery();%
% % %if&(result.next())&{&
% % % %gruppo%=%new&GruppoProxy();&
% % % %gruppo.setId(result.getLong("id")); % % % %%
% % % %gruppo.setNome(result.getString("nome"));%
% % %}%
% %}%catch&(SQLExcep-on&e)&{… % %} %%
% %return&gruppo;&
%}%
}%
Lazy Load: implementazione Proxy
public class GruppoProxy extends Gruppo {
private DataSource dataSource;

public GruppoProxy() {
this.dataSource = new DataSource();
}

public Set<Studente> getStudenti() {


Set<Studente> studenti = new HashSet<>();
Connection connection = this.dataSource.getConnection();
try {
PreparedStatement statement;
String query = "select * from studente where gruppo_id = ?";
statement = connection.prepareStatement(query);
statement.setLong(1, this.getId());
ResultSet result = statement.executeQuery();
while (result.next()) {
Studente studente = new Studente();
studente.setMatricola(result.getString("matricola"));
studente.setNome(result.getString("nome"));
studente.setCognome(result.getString("cognome"));
long secs = result.getDate("datanascita").getTime();
studente.setDataNascita(new java.util.Date(secs));
studenti.add(studente);
}
} catch (SQLException e) { …
}
this.setStudenti(studenti);
return super.getStudenti();
}
Lazy Load: osservazioni
•  Quando uso il "caricamento pigro" ?
–  Dipende dai casi d uso definiti in fase di
progettazione
•  Organizzazione del codice:
–  I Proxy appartengono al package della
persistenza (stesso package dove mettiamo
DAO)
Ricostruzione degli oggetti dal db

•  Sintetizzare il processo di ricostruzione degli oggetti dal db:


–  in base ai casi d'uso e stima della frequenza delle corrispondenti
operazioni decidere quando conviene seguire una strategia Lazy Load
–  per le classi con associazioni per le quali è stata scelta la strategia Lazy
Load applichiamo il pattern Proxy
–  per le classi con associazioni per le quali non conviene applicare la
strategia Lazy Load ricostruiamo la rete (parziale) di oggetti con i
risultati di un join
–  In fase di progettazione conviene quindi arricchire il nostro class
diagram con annotazioni sulle associazioni che indicano la strategia di
caricamento ("fetch") dei dati:
•  caricamento pigro (Lazy Load)
•  caricamento immediato (Eager): effettua un outer join
Esempio%di%riferimento%

Studente& Fetch:%lazy8load% Universita&


Gruppo& 1%
*% 8matricola:%String%
*% 8id:%Long%
8id:%Long% 8nome:%String% …%
8Nome:%String% 8cognome:%String% …%
Fetch:%lazy8load% 8dataNascita:%Date% Fetch:%eager% …%

gruppo& studente&
id% bigint% nullable=false% matricola% studente&
character(8)% nullable=false%
PK% PK%
matricola% character(8)% nullable=false%
nome% varchar(255)% nullable=false% nome% varchar(255)% PK%
nullable=false%
nome%
cognome% varchar(255)% nullable=false%

Universita& cognme%
data_nascita% varchar(255)%
date% nullable=false%
nullable=true%

id% bigint% nullable=false% data_nascita%


gruppo_id% date%
bigint% nullable=true%
FK%gruppo.id%
PK% universita_id% bigint% FK%universita.id%
…% …..% ……%
Rendere persistenti gli oggetti
•  L'operazione CREATE (save/insert/persist)
ha lo scopo di rendere persistenti gli
oggetti della applicazione
•  Problemi:
–  persistenza dei riferimenti
–  propagazione degli aggiornamenti
Persistenza dei riferimenti
•  Come rendiamo persistenti i riferimenti?
–  se il dominio offre identificatori naturali per
tutte le entità, il problema è limitato (ma
potremmo fare considerazioni su prestazioni e
portabilità)
–  se non tutte le classi hanno identificatori
naturali ("semplici") è necessario introdurre
chiavi surrogate
Persistenza dei riferimenti
•  Chiavi surrogate
–  la chiave può essere vista come una
rappresentazione persistente dell'OID
–  in parte cerchiamo di avvicinare i due mondi
(relazionale ed oggetti) senza stravolgerli,
armonizzando le differenze
•  Questa soluzione offre vantaggi in termini di
–  prestazioni (in un db relazionale, usando come chiavi
degli interi le operazioni di accesso sono più efficienti)
–  portabilità ed evoluzione (gli identificatori naturali
possono cambiare nel tempo*)
*Esempio: è allo studio una revisione del codice fiscale.
Persistenza dei riferimenti
•  Che cosa significa rendere persistente un
oggetto
–  se l'oggetto non esiste nel db l'operazione
corrisponde all'inserimento nel db dei suoi dati
–  se l'oggetto esiste, sono possibili diverse semantiche
•  Errore: si lancia un'eccezione
•  Update: si aggiornano i dati del db
•  Se nel db usiamo chiavi surrogate, come le
gestiamo?
–  è opportuno avere una rappresentazione della chiave
anche nel modello ad oggetti
–  per questo, ad ogni entità associamo esplicitamente
una variabile di istanza id (di tipo Long)
Persistenza dei riferimenti
•  Chi ci dà il valore di una chiave surrogata?
–  nb: la chiave viene creata al momento
dell'inserimento di un oggetto nel db
•  Diverse soluzioni:
–  soluzione naive: chiedo max al DBMS, lo
incremento ed eseguo l'inserimento usando tale
valore
–  uso campi auto-incrementanti
–  uso una sequenza SQL che interrogo per farmi
dare un nuovo id prima di ogni inserimento.
Persistenza dei riferimenti
•  Soluzione naive: chiedo al db max, lo incremento ed eseguo
l'inserimento usando tale valore
–  due operazioni (leggo il valore max, incremento e scrivo il nuovo
valore nel db): in un ambiente concorrente si possono creare situazioni
di errore
•  campi auto-incrementanti
–  dobbiamo aggiornare il valore della variabile id nell'oggetto, quindi dopo
l'inserimento dobbiamo interrogare il db per riottenerle: funziona, ma è
macchinoso (vedi librerie Jdbc per ottenere il valore del campo
autoincrementante)
•  sequenza SQL che interrogo per farmi dare un nuovo id prima di
ogni inserimento.
–  efficiente (il dbms offre anche funzionalità di caching)
–  semplice da implementare
–  qualche limite di portabilità (nonostante le sequenze siano standard
SQL, i vari DBMS differiscono un po' nell'uso di questo costrutto)
facilmente superabile scrivendo le istruzioni SQL in un file di
configurazione
Esempio (con chiave surrogata)
•  Consideriamo la classe Gruppo:
public class Gruppo {
private Long id;
private String nome;
private Set<Studente> studenti;

public Gruppo(){
this.studenti = new HashSet<>();
}

public Long getId() {


return this.id;
}
public void setId(Long id) {
this.id = id;
}
// seguono tutti gli altri metodi getter e setter
}
Persistenza dei riferimenti: esempio
public class GruppoDaoJDBC {
… Questo%ogge6o%non%%
private void save(Gruppo gruppo) { ha%un%id%

try { Genero%un%nuovo%id%
PreparedStatement statement = null; persistente%
long id = IdBroker.getId(connection);
gruppo.setId(id);
String insert = "insert into gruppo (id, nome) values (?,?)";
statement = connection.prepareStatement(insert);
statement.setLong(1, gruppo.getId());

statement.executeUpdate();
….
} catch (SQLException e) {

}
Persistenza dei riferimenti
•  Affidiamo la responsabilità della creazione
degli id ad una classe opportuna, che
chiamiamo IdBroker
IdBroker: esempio
%
public&class&IdBroker&{&
%
%//%Standard%SQL%(queste%stringhe%andrebbero%scri6e%in%un%file%di%configurazione%
%//%private%sta2c%final%String%query%=%"SELECT%NEXT%VALUE%FOR%SEQUENZA_ID%AS%id";%
%private&sta-c&final&String&query&=&"SELECT&nextval('sequenza_id')&AS&id";&&&//&postgresql&
%
%public&sta-c&Long&getId(Connec-on&connec-on)&{&
% %Long&id&=&null;&
% %try&{&
% % %PreparedStatement%statement%=%connec2on.prepareStatement(query);&
% % %logger.debug(statement);2
% % %ResultSet%result%=%statement.executeQuery();%
% % %result.next();%
% % %id%=%result.getLong("id");%
% % %logger.debug("Created2NEW2ID:2"2+2id);2
% %}%catch&(SQLExcep-on&e)&{&
% % %logger.error("Errore2SQL:2"2+2e.getMessage());2
% % %throw&new&PersistenceExcep-on(e.getMessage());&
% %}%
% %return&id;&
%}%
}
Nel%Database%
•  Statement SQL Per la creazione della
sequenza
(in Postgresql e Oracle):

CREATE SEQUENCE sequenza_id


START WITH 1 INCREMENT BY 1
MINVALUE 0
MAXVALUE 9999999
NO CYCLE;
Propagazione degli aggiornamenti
•  Quando effettuiamo una operazione di insert
(ma analogamente update o delete)
propaghiamo "in cascata" gli aggiornamenti a
tutta la rete di oggetti?
•  Ad esempio consideriamo la classe Gruppo:
–  quando rendiamo persistenti i dati di un gruppo,
propaghiamo l'operazione a tutti gli oggetti
Studente della collezione?
•  Anche in questo caso dipende dai casi d'uso
Propagazione degli aggiornamenti%
•  A6enzione%alle%prestazioni!%
•  Supponiamo%di%propagare%l'operazione%save()%di%
un%gruppo%a%tuV%gli%studen2%della%collezione:%%
–  dobbiamo%fare%save%di%tuV%gli%oggeV%della%collezione%
–  ma%alcuni%potrebbero%già%essere%presen2%nel%db%
–  quindi%prima%di%ogni%save()%dobbiamo%controllare%se%
l'ogge6o%è%già%stato%salvato%nel%database%
•  Vediamo%il%codice%
(prima%versione,%senza%concorrenza)%
%public&void&save(Gruppo&gruppo)&{&
% %Connec2on%connec2on%=%this.dataSource.getConnec-on();&
% %try&{&
% % %Long%id%=%IdBroker.getId(connecBon);2
% % %gruppo.setId(id);% % % %%
% % %String%insert%=%"insert%into%gruppo(id,%nome)%values%(?,?)";%
% % %PreparedStatement%statement%=%connec2on.prepareStatement(insert);%
% % %statement.setLong(1,%gruppo.getId());%
% % %statement.setString(2,%gruppo.getNome());%
& &&
% % %statement.executeUpdate();%
% % %//%salviamo%anche%tuV%gli%studen2%del%gruppo%in%CASCATA%
% % %this.updateStuden-(gruppo,&connec-on);&
%
% %}%catch&(SQLExcep-on&e)&{&
%
%private&void&updateStuden-(Gruppo&gruppo,&Connec-on&connec-on)&&
& & & & & & & & & & & &throws&SQLExcep-on&{&
% %StudenteDao%studenteDao%=%new&StudenteDaoJDBC();&
% %for&(Studente&studente&:&gruppo.getStuden-())&{&
% % %if&(studenteDao.findByPrimaryKey(studente.getMatricola())&==&null){&
% % % %studenteDao.save(studente);%
% % %}%
% % %String%update%=%"update%studente%SET%gruppo_id%=%?%WHERE%
matricola%=%?";%
% % %PreparedStatement%statement%=%connec2on.prepareStatement(update);%
% % %statement.setLong(1,%gruppo.getId());%
% % %statement.setString(2,%studente.getMatricola());%
% % %logger.debug(statement);&
% % %statement.executeUpdate();%
% %}%
%}%
Progettazione della Persistenza
•  Facendo%riferimento%al%class%diagram,%su%tu6e%le%
•  associazioni%indichiamo:%
–  %la%cardinalita’%
–  il%verso%secondo%cui%l'applicazione%naviga%la%
associazione%
–  la%strategia%di%fetch:%
•  Lazy%Load%vs.%Eager%(associata%al%verso)%
–  la%strategia%di%propagazione%degli%aggiornamen2:%
•  Cascade%vs.%No%cascade%(associata%al%verso)
Esempio%di%riferimento%
Fetch:%lazy8load%
Studente& Cascade:%none% Universita&
Gruppo& 1%
*% 8matricola:%String%
*% 8id:%Long%
8id:%Long% 8nome:%String% …%
8Nome:%String% 8cognome:%String% …%
Fetch:%lazy8load% 8dataNascita:%Date% Fetch:%eager% …%
Cascade:%persist,%delete% Cascade:%none%

gruppo& studente&
id% bigint% nullable=false% matricola% studente&
character(8)% nullable=false%
PK% PK%
matricola% character(8)% nullable=false%
nome% varchar(255)% nullable=false% nome% varchar(255)% PK%
nullable=false%
nome%
cognome% varchar(255)% nullable=false%

Universita& cognme%
data_nascita% varchar(255)%
date% nullable=false%
nullable=true%

id% bigint% nullable=false% data_nascita%


gruppo_id% date%
bigint% nullable=true%
FK%gruppo.id%
PK% universita_id% bigint% FK%universita.id%
…% …..% ……%

Potrebbero piacerti anche