Introduzione a Hibernate - Dipartimento di Informatica e Sistemistica

Introduzione a Hibernate
Versione preliminare
Antonella Poggi
Dipartimento di informatica e Sistemistica
Sapienza Università di Roma
Progetto di Applicazioni Software
Anno accademico 2008-2009
Progetto di Applicazioni Software
Anno accademico 2008-2009
Riferimenti bibliografici
• Libro “Java Persistence with Hibernate”, Christian
Bauer and Gavin King - Editore: Manning
• Hibernate Reference Documentation (cf. link sulla
pagina web del corso)
A. Poggi
1
Progetto di Applicazioni Software
Anno accademico 2008-2009
Argomenti principali
• Introduzione ad Hibernate e suoi principali utilizzi
• Le basi della gestione della persistenza con Hibernate
• Mapping più complessi
• Le transazioni e il ciclo di vita degli oggetti
• Creare un livello generico per la gestione della
persistenza
A. Poggi
2
Progetto di Applicazioni Software
Anno accademico 2008-2009
Introduzione ad Hibernate
e suoi principali utilizzi
A. Poggi
3
Progetto di Applicazioni Software
Anno accademico 2008-2009
Hibernate core
• Anche noto come Hibernate o Hibernate 3.3.x
• Software open source e free, distribuito sotto licenza
GNU
• ORM libreria per Java: servizio di base per la gestione
della persistenza in applicazioni Java
→ fornisce un framework per mappare un modello di
dominio object-oriented su di una base di dati
relazionale database
→ caratteristica primaria: mapping da classi Java a
tabelle di una base di dati (e mapping da tipi di dati
Java a tipi di dati SQL)
+ abilità di interrogare la basi di dati
A. Poggi
4
Progetto di Applicazioni Software
Anno accademico 2008-2009
• Il codice dell’applicazione usa le API di Hibernate per
gestire la persistenza
• I mapping tra classi Java e tabelle relazionali sono
specificati in appositi file XML
→ in linea di principio, lo sviluppatore è esonerato da:
– richieste di esecuzione di chiamate SQL
– gestione manuale dei risultati di chiamate SQL e
loro eventuale conversione in oggetti
• L’applicazione rimane portabile in tutti i sistemi di
gestione supportati, con pochissimo overhead
• Può essere usato in maniera independente dal tipo di
applicazione, di piattaforma e di ambiente runtime, con
tutti i JDK
A. Poggi
5
Progetto di Applicazioni Software
Anno accademico 2008-2009
Hibernate tools
• Insieme di strumenti per Hibernate 3
• Forniscono dei task Ant e dei plugin Eclipse per fare
reverse engineering, generazione di codice, e, più in
generale, per interagire con Hibernate
– Ant: strumento per fare il build, la compilazione e il
deployment di applicazioni Java complesse
– Eclipse: ambiente di sviluppo open-source, molto
flessibile
A. Poggi
6
Progetto di Applicazioni Software
Anno accademico 2008-2009
Tipici utilizzi di Hibernate
• Top down
• Bottom up
• Meet in the middle
A. Poggi
7
Progetto di Applicazioni Software
Anno accademico 2008-2009
Tipici utilizzi di Hibernate: Top Down
• Si parte da un diagramma delle classi di dominio e dalla
sua implementazione in Java e si ha completa libertà
rispetto allo schema della base di dati
• Si specificano i file XML con i mapping ed
eventualmente, si può usare Hibernate (in particolare lo
strumento Hibernate hbm2ddl) per generare lo schema
della base di dati
– vantaggi: comodo quando non vi è alcun database
preesistente
– svantaggi: diventa laborioso se si vogliono sfruttare
tutte le caratteristiche del DBMS, e.g. stored
procedure, trigger
A. Poggi
8
Progetto di Applicazioni Software
Anno accademico 2008-2009
Tipici utilizzi di Hibernate: Bottom up
• Si parte da un base di dati esistente legacy e si ha
completa libertà rispetto al dominio dell’applicazione
• Si usa una serie di strumenti Hibernate per generare lo
schema della base il codice Java per la gestione della
persistenza, ovvero il codice delle classi persistenti
– jdbcconfiguration: strumento che si connette via
JDBC alla base di dati ed accede ai metadati dal
catalogo della base di dati
– hbm2hbmxml: strumento che prende in ingresso i
metadati ottenuti dal catalogo e genera i file XML
dei mapping
– hbm2cfgxml: strumento che prende in ingresso i
A. Poggi
9
Progetto di Applicazioni Software
Anno accademico 2008-2009
metadati ottenuti dal catalogo e genera il file di
configurazione di Hibernate (cf. slides più avanti)
• Problemi:
– lo schema della base di dati deriva, nella migliore
delle ipotesi, da un “degrado” dello schema ER
⇒ non si potrà mai ottenere un diagramma delle
classi che sfrutti caratteristiche importanti della
programmazione ad oggetti, e.g. ereditarietà,
polimorfismo
– la logica dell’applicazione è influenzata dalla
rappresentazione dei dati ⇒ il diagramma delle classi
di dominio conterrà solamente le classi persistenti
A. Poggi
10
Progetto di Applicazioni Software
Anno accademico 2008-2009
Tipici utilizzi di Hibernate: Meet in the
middle
• Scenario più interessante e comune - quello del
progetto richiesto per questo corso!
• Si parte da un diagramma delle classi di dominio
dell’applicazione e da una base di dati preesistente
• Non è in generale possibile mappare un insieme di classi
di dominio su di una base di dati arbitrari
→ si deve seguire un approccio generico all’ORM e
ricorrere ad Hibernate quando possibile, ed in caso
contrario, all’approccio generico DAO
N.B. Nella trattazione che faremo, assumeremo di usare
Hibernate seguendo l’approccio “Meet in the middle”.
A. Poggi
11
Progetto di Applicazioni Software
Anno accademico 2008-2009
Le basi della gestione
della persistenza con
Hibernate
A. Poggi
12
Progetto di Applicazioni Software
Anno accademico 2008-2009
Hibernate in azione
5 principali ingredienti di un’applicazione che fa uso di
Hibernate per la gestione della persistenza
• le classi di dominio realizzate in Java
• una base di dati, e.g. realizzata in Mysql
• un file che definisce il mapping di ogni classe persistente
• uno o più file di configurazione di Hibernate
• le interfacce Hibernate per l’accesso alla base di dati:
Session, Transaction e Query - package
org.hibernate
A. Poggi
13
Progetto di Applicazioni Software
Anno accademico 2008-2009
Le classi di dominio
• Le classi di dominio sono definite come in qualsiasi
applicazione Java
→ tipicamente, metodi set e get per l’accesso in
scrittura e lettura delle proprietà degli oggetti della
classe
• metodi di business della classe
A. Poggi
14
Progetto di Applicazioni Software
Anno accademico 2008-2009
Diagramma delle classi di dominio - Esempio
vincente
0..1
0..*
relativa
Offerta
ammontare : int
dataCreazione: Date
Item
nome : String
codice : int
descrizione : String
prezzoIniziale: int
giornoInizio: Date
giornoFine: Date
calcolaTempoResiduo(): int
A. Poggi
0..*
fattaDa
Indirizzo
User
0..*
compratoDa
nome : String
cognome : String
username: String
password: String
email: String
calcolaCostoSpedi
zione(): int
risiede
via : String
civico : int
cap: String
città: String
15
Progetto di Applicazioni Software
Anno accademico 2008-2009
Le classi di dominio - Esempio
package bid;
import java.util.StringTokenizer;
public class User {
private
private
private
private
private
private
String nome;
String cognome;
String username;
String password;
String email;
Indirizzo indirizzo;
public User(){}
public User(String user){
username=user;
A. Poggi
16
Progetto di Applicazioni Software
Anno accademico 2008-2009
}
public String getNome() { return nome;}
public void setNome(String nome) { this.nome=nome;}
public String getCognome() { return cognome;}
public void setCognome(String cognome) { this.cognome=cognome;}
public String getUsername() { return username;}
public void setUsername(String username) { this.username=username;}
public String getPassword() { return password;}
public void setPassword(String password) { this.password=password;}
public String getEmail() { return email;}
public void setEmail(String email) { this.email=email;}
public Indirizzo getIndirizzo() { return indirizzo;}
public void setIndirizzo(Indirizzo indirizzo) { this.indirizzo=indirizzo
public int calcolaCostoSpedizione {
A. Poggi
17
Progetto di Applicazioni Software
Anno accademico 2008-2009
// funzione che restituisce il costo della spedizione
}
}
A. Poggi
18
Progetto di Applicazioni Software
Anno accademico 2008-2009
Come organizzare il codice di un’applicazione
che usa Hibernate(1)
WORKDIR
+ lib
<Hibernate ed altre librerie>
+ src
+bid
User.java
+ bin
A. Poggi
19
Progetto di Applicazioni Software
Anno accademico 2008-2009
La base di dati
• È costituita da un insieme di:
– tabelle
– vincoli
– store procedure, trigger
• È mantenuta all’interno di un certo DBMS, e.g. Mysql,
provvisto del proprio driver jdbc
• Può essere acceduta da un certo utente (con relativi
privilegi)
A. Poggi
20
Progetto di Applicazioni Software
Anno accademico 2008-2009
Tabelle della base di dati - Esempio
CREATE TABLE utente (
username VARCHAR(10) PRIMARY KEY,
nome VARCHAR(15),
cognome VARCHAR(15),
cod_fis VARCHAR(16) UNIQUE,
password VARCHAR(8),
email VARCHAR(20),
via VARCHAR(20),
civico INT,
cap VARCHAR(5),
citta VARCHAR(15));
A. Poggi
21
Progetto di Applicazioni Software
Anno accademico 2008-2009
File di mapping per le classi persistenti
• File XML che definisce come si mappano le proprietà
delle classi Java persistenti sulle tabelle della base di
dati
• Deve soddisfare la grammatica specificata all’interno di
un apposito DTD, chiamato
hibernate-mapping-3.0.dtd
N.B. Per verificare la correttezza sintattica del file
XML, Hibernate cercherà il DTD all’interno del
classpath e lo troverà nella libreria .jar di Hibernate
(ammesso che il classpath la includa) - qualora non
dovesse trovare il DTD, Hibernate lo cercherà
all’indirizzo specificato nella dichiarazione del
DOCTYPE
A. Poggi
22
Progetto di Applicazioni Software
Anno accademico 2008-2009
File di mapping - Esempio
File User.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="User" table="UTENTE">
<id name="username" column="username"></id>
<property name="nome" column="NOME"></property>
<property name="cognome" column="COGNOME"></property>
<property name="email"></property>
<property name="password"></property>
</class>
</hibernate-mapping>
A. Poggi
23
Progetto di Applicazioni Software
Anno accademico 2008-2009
File di mapping - Commenti
• L’elemento class dichiara il nome della classe
persistente e la tabella a cui essa corrisponde
→ dice ad Hibernate come rendere persistente e
caricare gli oggetti della classe User sfruttando la
tabella UTENTE, ovvero ogni istanza è rappresentata da
una tupla della tabella
• L’elemento id dichiara:
– il nome della proprietà della classe che gioca il ruolo
di identificatore
→ Importante:
∗ una classe persistente è sempre caratterizzata da
una proprietà che ne identifica le istanze
∗ una volta creato un oggetto della classe, tale
A. Poggi
24
Progetto di Applicazioni Software
Anno accademico 2008-2009
proprietà non potrà essere modificata
dall’applicazione (il corrispondente metodo set
può essere definito privato)
– il nome dell’attributo della tabella che identifica le
tuple mappate su oggetti
• L’elemento property dichiara il nome delle proprietà
persistenti della classe e a quali attributi della tabella
corrispondono; di default, nessuna proprietà è
persistente
→ Importante: come per la proprietà che identifica gli
oggetti, il nome della proprietà indica ad Hibernate
quali metodi set e get usare
A. Poggi
25
Progetto di Applicazioni Software
Anno accademico 2008-2009
Come organizzare il codice di un’applicazione
che usa Hibernate(2)
WORKDIR
+ lib
<Hibernate ed altre librerie>
+ src
+ bid
User.java
User.hbm.xml
+ bin
A. Poggi
26
Progetto di Applicazioni Software
Anno accademico 2008-2009
File di configurazione per Hibernate
• Hibernate costituisce la parte dell’applicazione che
gestisce la persistenza, ovvero che si connette alla base
di dati
→ ha bisogno di avere le informazioni necessarie ad
effettuare la connessione, quali il DBMS, il driver
JDBC, la base di dati, utente/password, etc.
• Un file XML fornisce tutte queste informazioni
• Tale file deve soddisfare la grammatica specificata in
nel DTD hibernate- configuration-3.0.dtd
A. Poggi
27
Progetto di Applicazioni Software
Anno accademico 2008-2009
File di configurazione per Hibernate Esempio
File hibernate.cfg.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- Database connection settings -->
<property name="hibernate.connection.driver_class">
com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">
jdbc:mysql://localhost/bid</property>
<property name="connection.username">root</property>
<property name="connection.password"></property>
<!-- SQL dialect -->
A. Poggi
28
Progetto di Applicazioni Software
Anno accademico 2008-2009
<property name="dialect">
org.hibernate.dialect.MySQL5InnoDBDialect</property>
<!-- JDBC connection pool (use C3P0) -->
<property name="c3p0.min_size">5</property>
<property name="c3p0.max_size">20</property>
<property name="c3p0.timeout">300</property>
<property name="c3p0.max_statement">50</property>
<!-- Show and print nice SQL on stdout -->
<property name="show_sql">true</property>
<property name="format_sql">true</property>
<!-- List of XML mapping files -->
<mapping resource="bid/User.hbm.xml" />
</session-factory>
</hibernate-configuration>
A. Poggi
29
Progetto di Applicazioni Software
Anno accademico 2008-2009
File di configurazione per Hibernate Commenti
• L’elemento session-factory definisce le impostazioni
per l’accesso ad una particolare base di dati
• Le proprietà il cui nome ha la forma
(hibernate).connection.* contengono le
informazioni necessarie per impostare la connessione
JDBC
• La proprietà dialect specifica la variante di SQL che
deve generare Hibernate
• Le proprietà c3p0.* indicano le impostazioni necessarie
a configurare il software c3p0 per il pooling di
connessioni
A. Poggi
30
Progetto di Applicazioni Software
Anno accademico 2008-2009
– min size: numero minimo di connessioni che deono
essere pronte in ogni momento
– max size: numero massimo di connessioni aperte
gestite dal pool
– timeout: tempo al termine del quale una
connessione aperta non più usata viene rimossa
– max statements: numero di prepared statements
che sono mentenuti in cache
A. Poggi
31
Progetto di Applicazioni Software
Anno accademico 2008-2009
Come organizzare il codice di un’applicazione
che usa Hibernate(3)
WORKDIR
+ lib
<Hibernate ed altre librerie>
+ src
+ bid
User.java
User.hbm.xml
hibernate.cfg.xml
+ bin
A. Poggi
32
Progetto di Applicazioni Software
Anno accademico 2008-2009
Le interfacce Hibernate
Hibernate fornisce tre principali interfacce per l’accesso alla
base di dati, tutte appartenenti al package org.hibernate
• Session: ogni istanza rappresenta una sessione di
comunicazione tra applicazione e base di dati
→ comprende i metodi per salvare/caricare oggetti
nella/dalla base di dati
• Transaction: ogni istanza rappresenta una transazione
→ maggiore disaccoppiamento dell’applicazione: non è
necessario usare l’API JDBC per impostare una
transazione
→ gioca al ruolo di gestore di transazioni in un sistema
che accede a più basi di dati all’interno di un’unica
unità di lavoro di sistema
A. Poggi
33
Progetto di Applicazioni Software
Anno accademico 2008-2009
• Query: interfaccia che permette di creare ed eseguire
query
– sia nel linguaggio di query di Hibernate (HQL) che
in SQL
– che usino al loro interno delle variabili speciali
→ utile in tutti quei casi in cui era necessario ricorre ad
un DCS nell’approccio DAO
A. Poggi
34
Progetto di Applicazioni Software
Anno accademico 2008-2009
Interfacce Hibernate - Esempio d’uso
File Bid.java
package bid;
import java.util.*;
import org.hibernate.*;
import persistence.HibernateUtil;
public class Bid {
public static void main(String[] args) {
//First unit of work
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
User user = new User("pippo");
String userId = (String) session.save(user);
tx.commit();
session.close();
A. Poggi
35
Progetto di Applicazioni Software
Anno accademico 2008-2009
//Second unit of work
session = HibernateUtil.getSessionFactory().openSession();
tx = session.beginTransaction();
user = (User) session.get(User.class,userId);
user.setNome("Filippo");
tx.commit();
session.close();
//Third unit of work
session = HibernateUtil.getSessionFactory().openSession();
tx = session.beginTransaction()
List users = session.createSQLQuery("select * from utente
order by username").addEntity(User.class).list();
System.out.println(users.size()+" user(s) found: ");
for (Iterator iter= users.iterator(); iter.hasNext(); ) {
User userId = (User) iter.next();
System.out.println (userId.getNome());
}
tx.commit();
A. Poggi
36
Progetto di Applicazioni Software
Anno accademico 2008-2009
session.close();
//Shutting down the application
HibernateUtil.shutdown();
}
}
A. Poggi
37
Progetto di Applicazioni Software
Anno accademico 2008-2009
Interfacce Hibernate - Commenti
• L’utilizzo dell’interfaccia Transaction imposta
l’autocommit a false → necessità di chiamare il metodo
commit
• Tutti i comandi SQL sono generati a runtime (o
eventualmente all’avvio dell’applicazione per tutti quei
comandi “riusabili”)
• La prima unità di lavoro, quando eseguita, corrisponde
ad effettuare sulla base di dati un comando SQL simile
a
insert into UTENTE(USERNAME,NOME,COGNOME,COD_FIS,
PASSWORD,EMAIL,VIA,CIVICO,CAP,CITTA)
values (’pippo’, null, null, null,
A. Poggi
38
Progetto di Applicazioni Software
Anno accademico 2008-2009
null, null, null, null, null, null);
• La seconda unità di lavoro mostra l’abilità di Hibernate
di:
– caricare un oggetto dalla base di dati, a partire dalla
classe di cui è istanza e dal suo identificatore
→ il metodo get(Class,Serializable) restituisce
un’istanza della classe persistente data in input
caratterizzata dall’identificatore in input, e null se
la suddetta istanza non esiste
N.B. Richiede sia definito il costruttore senza
argomenti per la classe persistente
– effettuare un dirty checking automatico: senza che
sia richiesto esplicitamente di fare l’update
dell’istanza, Hibernate si “accorge” delle modifiche
apportate sulla proprietà nome ed aggiorna
A. Poggi
39
Progetto di Applicazioni Software
Anno accademico 2008-2009
automaticamente la base di dati
• La terza unità di lavoro mostra come è possibile
“incapsulare” una query di accesso alla base di dati e
ricavare dai risultati oggetti del dominio
→ questo tipo di accesso deve essere limitato a quei
casi in cui si usano i DCS nell’approccio DAO, ovvero,
essenzialmente, ai casi in cui è necessario effettuare una
ricerca sulla base di dati non di tipo “diretto”, e.g.
FindByCriteria
A. Poggi
40
Progetto di Applicazioni Software
Anno accademico 2008-2009
Inizializzazione di Hibernate - SessionFactory
• Per inizializzare Hibernate, si costruisce un oggetto
SessionFactory a partire da un oggetto
Configuration
• un oggetto Configuration, sostanzialmente,
rappresenta il file di configurazione di Hibernate
SessionFactory sessionFactory = new Configuration().
configure().buildSessionFactory();
– quando new Configuration() è chiamato,
Hibernate cerca un file chiamato
hibernate.properties (cf. esercitazione) e tutte
le impostazioni lı̀ definite sono associate all’oggetto
di Configuration
A. Poggi
41
Progetto di Applicazioni Software
Anno accademico 2008-2009
– quando configure() è chiamato, Hibernate cerca il
file hibernate.cfg.xml
– la locazione in cui Hibernate cerca i file suddetti è
quella specificata nel classpath ⇒ se si vuole usare
un’altra locazione la si deve specificare in input
– esistono altre maniere di impostare delle proprietà
della connessione (cf. documentazione)
• nella maggior parte delle applicazioni, SessionFactory
deve essere istanziato una sola volta durante la fase di
inizializzazione di Hibernate, e gioca il ruolo di gestore
delle sessioni, nel senso che ogni istanza di Session
deve essere creata a partire da lui
⇒ è buona prassi realizzare una classe HibernateUtil
per l’inizializzazione di Hibernate e la gestione della
singola istanza di SessionFactory
A. Poggi
42
Progetto di Applicazioni Software
Anno accademico 2008-2009
Gestore delle sessioni - HibernateUtil
File HibernateUtil.java
package persistence;
import org.hibernate.*;
import org.hibernate.cfg.*;
public class HibernateUtil {
private static SessionFactory sessionFactory = initHibernateUtil();
private static SessionFactory initHibernateUtil(){
try {
return new Configuration().configure().buildSessionFactory();
} catch (HibernateException ex) {
throw new ExceptionInInitializerError(ex);
}
}
A. Poggi
43
Progetto di Applicazioni Software
Anno accademico 2008-2009
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
public static void shutdown() {
//Close caches and connection pools
getSessionFactory().close();
}
}
A. Poggi
44
Progetto di Applicazioni Software
Anno accademico 2008-2009
Come organizzare il codice di un’applicazione
che usa Hibernate(4)
WORKDIR
+ lib
<Hibernate ed altre librerie>
+ src
+bid
User.java
User.hbm.xml
Bid.java
+ persistence
HibernateUtil.java
hibernate.cfg.xml
+ bin
A. Poggi
45
Progetto di Applicazioni Software
Anno accademico 2008-2009
I mapping
A. Poggi
46
Progetto di Applicazioni Software
Anno accademico 2008-2009
In poche parole...
I file di mapping permettono di definire:
• come le classi Java che realizzano classi di dominio
UML persistenti siano mappate sugli elementi della
base di dati
• informazioni sugli elementi stessi della base di dati che
vengono mappati
→ queste sono utili nell’approccio top-down, ovvero
quando a partire dalle classi di dominio e dai mapping si
intende generare la base di dati sottostante - in questo
corso non approfondiremo questo argomento
A. Poggi
47
Progetto di Applicazioni Software
Anno accademico 2008-2009
Note importanti
1. In questa trattazione, assumeremo che la porzione
persistente del diagramma delle classi e lo schema ER
siano “simili”, nel senso che:
• la realizzazione di una classe di dominio persistente
è mappata su una ed un’unica tabella
• le realizzazioni di due classi di dominio persistenti in
ISA sono mappate su due tabelle legate da una
foreign key
• le realizzazioni di due classi di dominio persistenti
legate da un’associazione sono mappate su due
tabelle legate da una o più catene di vincoli di
foreign key/inclusione (che eventualmente
coinvolgono ulteriori tabelle)
A. Poggi
48
Progetto di Applicazioni Software
Anno accademico 2008-2009
Per tutti i casi di mapping che non rientrano tra i tipi di
cui sopra, Hibernate fornisce delle soluzioni non
soddisfacibili
→ è pertanto necessario ricorrere all’approccio DAO
A. Poggi
49
Progetto di Applicazioni Software
Anno accademico 2008-2009
2. la realizzazione in java del diagramma delle classi di
dominio segue le regole viste nel corso di Progettazione
del Software, con le seguenti principali eccezioni,
riguardanti i campi privati persistenti che sono
collezioni:
• si definisce il metodo set come per gli altri tipi di campi
- ma attenzione: lo si definisce private
→ permette ad Hibernate, ma non al client, di usarlo
• il metodo get non restituisce un clone della collezione,
bensı̀ la collezione stessa (restrizione di Hibernate)
A. Poggi
50
Progetto di Applicazioni Software
Anno accademico 2008-2009
Mappare una classe Java significa...
• stabilire una corrispondenza tra le sue istanze e le tuple
di una tabella della base di dati
• mappare il suo identificatore
• mappare le sue proprietà persistenti
– proprietà che sono valori (eventualmente insiemi di
attributi/valori, ovvero con molteplicità massima
diversa da 1)
– proprietà che sono istanze di un’altra classe di
dominio Java (eventualmente insiemi di istanze,
ovvero con molteplicità massima diversa da 1)
A. Poggi
51
Progetto di Applicazioni Software
Anno accademico 2008-2009
Mappare gli identificatori delle classi di
dominio
La proprietà che identifica una classe è una proprietà
“speciale”
• non può essere modificata una volta che è stata
assegnata ad un’istanza
• dal punto di vista sintattico, una proprietà che
identifica una classe deve essere mappata attraverso
l’elemento XML <id> (che DEVE essere definito)
A. Poggi
52
Progetto di Applicazioni Software
Anno accademico 2008-2009
– <id> può avere un elemento figlio <generator> che
DEVE un attributo class che specifica una classe
Java che implementa l’interfaccia
org.hibernate.id.IdentifierGenerator e che
viene usata per generare identificatori unici; se tale
classe prende dei parametri in ingresso, vengono
specificati per mezzo di elementi figli param;
Hibernate viene fornito con delle classi built-in per
generare identificatori, tra le altre:
∗ assigned significa che l’applicazione assegna
l’identificatore all’istanza prima che sia chiamato il
metodo save - questo è il comportamento di default se
generator non compare
∗ native: usa i generatori di identificatori forniti dal
DBMS sottostante - mantiene il mapping portabile
A. Poggi
53
Progetto di Applicazioni Software
Anno accademico 2008-2009
Mappare proprietà che sono valori con
molteplicità massima pari a 1
Abbiamo già visto come si definisce nel file di mapping
come mappare una proprietà di una classe che è un valore
→ Si usa l’elemento XML property
Ora diciamo qualcosa in più sul sistema di tipi di
Hibernate....
A. Poggi
54
Progetto di Applicazioni Software
Anno accademico 2008-2009
Il sistema di tipi di Hibernate
• I tipi di Hibernate sono di fatto dei convertitori che
traducono da tipi Java a tipi SQL e viceversa
• La maggior parte dei tipi built-in di Hibernate hanno lo
stesso nome del tipo Java che mappano
• Possono però esserci più tipi Hibernate per uno stesso
tipo Java
→ bisogna allora indicare nel mapping il tipo di
mapping di Hibernate da usare
• I tipi built-in di Hibernate non possono essere usati per
fare conversioni di qualsiasi tipo (e.g. da (SQL)
VARCHAR a (Java) Integer), ma si possono definire dei
tipi appositi per effettuare conversioni “ad-hoc”
A. Poggi
55
Progetto di Applicazioni Software
Anno accademico 2008-2009
Tipi primitivi di Hibernate: corrispondenze
SQL-Java
Tipo Hibernate
Tipo SQL standard
Tipo Java
integer
INTEGER
int or java.lang.Integer
long
BIGINT
long or java.lang.Long
integer
SMALLINT
short or java.lang.Integer
float
FLOAT
float or java.lang.Float
double
DOUBLE
double or java.lang.Double
big_decimal
NUMERIC
java.math.BigDecimal
character
CHAR(1)
java.lang.String
string
VARCHAR (..)
java.lang.String
byte
TINYINT
float or java.lang.Float
boolean
BIT
boolean or java.lang.Boolean
A. Poggi
56
Progetto di Applicazioni Software
Anno accademico 2008-2009
Tipi di dati Hibernate sulle date e il tempo
Tipo Hibernate
Tipo SQL standard
Tipo Java
date
DATE
java.util.Date or java.sql.Date
time
TIME
java.util.Time or java.sql.Time
timestamp
TIMESTAMP
java.util.Timestamp
or java.sql.Timestamp
calendar
TIMESTAMP
java.util.Calendar
calendar_date
DATE
java.util.Calendar
A. Poggi
57
Progetto di Applicazioni Software
Anno accademico 2008-2009
Come usare i tipi di Hibernate nei mapping
(Esempio)
<class name="Item" table="ITEM">
(...)
<property name="giornoInizio" type="time"
column="INIZIO"/>
</class>
N.B. Se non specifichiamo il tipo di Hibernate, allora
Hibernate usa la reflection, ovvero risale al tipo Java della
proprietà giornoInizio della classe Item e assume un
comportamento di default
→ se abbiamo definito la proprietà giornoInizio come di
tipo java.util.Date, Hibernate cerca di convertirla nel
tipo TIMESTAMP (6= TIME)
A. Poggi
58
Progetto di Applicazioni Software
Anno accademico 2008-2009
Classi che realizzano tipi UML
• A valle della fase di progetto, sono spesso individuati
dei tipi UML che sono realizzati tramite classi Java, da
importare o definite nell’applicazione stesse
• Le istanze di una classe che realizza un tipo UML:
– sono proprietà di classi di dominio
– non possono essere condivise
– non sono identificate da nessuna proprietà
• Per le classi che realizzano un nuovo tipo UML, è
necessario ridefinire i metodi toString, equals(),
hashCode() e, se qualche funzione della classe Java
effettua side-effect, clone()
A. Poggi
59
Progetto di Applicazioni Software
Anno accademico 2008-2009
Mapping di proprietà istanze di una classe
che realizza un tipo UML
Ipotesi: come sempre, le colonne da mappare appartengono
alla stessa tabella su cui è mappata la classe
A seconda che sia o no necessario effettuare una
conversione od una forma di type checking tra i valori delle
proprietà della classe che realizza il tipo UML, ed il valore
degli attributi della base di dati mappati, si usa un diverso
approccio
A. Poggi
60
Progetto di Applicazioni Software
Anno accademico 2008-2009
Caso 1: Uso dell’elemento component
L’elemento XML component serve per mappare le proprietà
di una classe Java definita dall’utente che:
• non è una classe di dominio
• deve essere resa persistente nella stessa tabella sulla
quale si definisce il mapping per la classe per la quale
definisce il mapping l’elemento padre dell’elemento
component (N.B. si può anche fare un uso annidato
dell’elemento component)
→ component può essere usato quando non si ha bisogno di
effettuare alcuna conversione di tipo
A. Poggi
61
Progetto di Applicazioni Software
Anno accademico 2008-2009
Esempio: classe Indirizzo
Ricordiamo che la classe User.java aveva una proprietà
indirizzo di tipo Indirizzo. La classe Indirizzo è cosı̀
definita:
package bid;
public class Indirizzo {
private String via;
private String cap;
private int numeroCivico;
private String citta;
public Indirizzo(){}
public String getVia() { return via;}
public void setVia(String via) { this.via=via;}
public String getCap() { return cap;}
A. Poggi
62
Progetto di Applicazioni Software
Anno accademico 2008-2009
public void setCap(String cap) { this.cap=cap;}
public int getNumeroCivico() { return numeroCivico;}
public void setNumeroCivico(int numero) { this.numeroCivico=numero;}
public String getCitta() { return citta;}
public void setCitta(String citta) { this.citta=citta;}
}
A. Poggi
63
Progetto di Applicazioni Software
Anno accademico 2008-2009
Esempio: mapping della proprietà indirizzo
di tipo Indirizzo
Nel file di mapping User.hbm.xml mappiamo la proprietà
indirizzo come segue:
<hibernate-mapping>
<class name="bid.User" table="UTENTE">
(...)
<component name="indirizzo" class="bid.Indirizzo">
<property name="via" column="via"></property>
<property name="numeroCivico" column="civico"></property>
<property name="cap" column="cap"></property>
<property name="citta" column="citta"></property>
</component>
</class>
</hibernate-mapping>
A. Poggi
64
Progetto di Applicazioni Software
Anno accademico 2008-2009
Caso 2: Uso dei Custom types di Hibernate
Hibernate permette di definire dei tipi Hibernate ad-hoc nei
casi in cui i tipi built-in non siano adeguati
• sono fornite diverse interfacce più o meno complesse a
seconda del tipo di conversione da effettuare
→ è necessario definire una classe Java che implementa
una delle interfacce e che effettua la conversione
da/verso valori della classe Java che realizza il tipo
verso/da valori nella base di dati
• l’interfaccia più comunemente usata è
org.hibernate.usertype.UserType: fornisce i metodi
di base per effettuare il caricamento ed il salvataggio di
istanze di classi che realizzano tipi di dato
A. Poggi
65
Progetto di Applicazioni Software
Anno accademico 2008-2009
• un’altra interfaccia è
org.hibernate.usertype.CompositeUserType:
fornisce inoltre metodi per esporre/modificare proprietà
“interne” di istanze di classi che realizzano tipi UML non rientra negli scopi del corso
A. Poggi
66
Progetto di Applicazioni Software
Anno accademico 2008-2009
Interfaccia org.hibernate.usertype.UserType
(Esempio)
Supponiamo che per le proprietà giornoInizio e
giornoFine della classe di dominio Item, si decida di
evitare l’uso del tipo java.sql.Date e si voglia piuttosto
usare il tipo GregorianCalendar
→ si deve definire una classe che effettua la conversione dal
tipo sql DATE degli attributi inizio e fine della tabella
ITEM al tipo GregorianCalendar
A. Poggi
67
Progetto di Applicazioni Software
Anno accademico 2008-2009
Nel file di mapping Item.hbm.xml si definisce come
mappare le proprietà si cui sopra come segue:
<hibernate-mapping>
<class name="bid.Item" table="ITEM">
(...)
<property name="giornoInizio" column="inizio"
type="persistence.GregorianCalendarUserType"/>
<property name="giornoFine" column="fine"
type="persistence.GregorianCalendarUserType"/>
</class>
</hibernate-mapping>
A. Poggi
68
Progetto di Applicazioni Software
Anno accademico 2008-2009
Esempio: classe che implementa un nuovo
tipo di Hibernate
Implementiamo la classe
persistence.GregorianCalendarUserType che realizza il
nuovo tipo di Hibernate per fare la conversione da/verso
GregorianCalendar ed il tipo SQL DATE
package persistence;
import
import
import
import
import
java.io.Serializable;
java.sql.PreparedStatement;
java.sql.ResultSet;
java.sql.SQLException;
java.util.*;
import org.hibernate.*;
import org.hibernate.usertype.*;
A. Poggi
69
Progetto di Applicazioni Software
Anno accademico 2008-2009
public class GregorianCalendarUserType implements UserType{
//indica ad Hibernate i tipi SQL da mappare - possono essere più di uno
public int[] sqlTypes(){
return new int[] {Hibernate.DATE.sqlType()};
}
//indica ad Hibernate il tipo Java da mappare
public Class returnedClass() {
return GregorianCalendar.class;
}
//indica se il tipo Java è immutabile
//utile per effettuare eventuali ottimizzazioni
public boolean isMutable() {
return false;
}
//per eseguire il dirty checking, il tipo Hibernate usa i seguenti
//metodi, che tipicamente delegano ai metodi corrispondenti del tipo Jav
public boolean equals(Object x, Object y) throws HibernateException {
if (x==y) return true;
if (x==null || y==null) return false;
return x.equals(y);
A. Poggi
70
Progetto di Applicazioni Software
Anno accademico 2008-2009
}
public int hashCode(Object x) throws HibernateException {
return x.hashCode();
}
//restituisce una copia del valore del tipo Java - se il tipo
//è immutabile, è sufficiente restituire il riferimento all’oggetto
public Object deepCopy(Object value) throws HibernateException {
if (value==null)
return value;
else {
GregorianCalendar g= (GregorianCalendar)
((GregorianCalendar) value).clone();
return g;
}
}
//metodo che converte e restituisce i valori ottenuti dal JDBC
//ResultSet nel valore della proprietà Java corrispondente
public Object nullSafeGet(ResultSet resultSet, String[] names, Object ow
throws HibernateException,SQLException {
String s = resultSet.getDate(names[0]).toString();
int year=Integer.parseInt(s.substring(0,4));
A. Poggi
71
Progetto di Applicazioni Software
Anno accademico 2008-2009
int month=Integer.parseInt(s.substring(5,7));
int day=Integer.parseInt(s.substring(8,10));
return new GregorianCalendar(year,month,day);
}
//metodo che converte e scrive il valore della proprietà Java del
//tipo sul JDBC PreparedStatement che esegue il salvataggio
public void nullSafeSet(PreparedStatement statement, Object value, int i
throws HibernateException, SQLException {
if (value==null){
statement.setNull(index,Hibernate.DATE.sqlType());
}
else {
GregorianCalendar g= (GregorianCalendar) value;
String s = g.get(Calendar.YEAR)+"-"+g.get(Calendar.MONTH)+"-"+
g.get(Calendar.DAY_OF_MONTH);
statement.setDate(index,java.sql.Date.valueOf(s));
}
}
//metodo chiamato per effettuare il merge di ogetti in stato
//"detached" - cf. lezione più avanti - se il tipo
//è immutabile, è sufficiente restituire il riferimento all’oggetto
A. Poggi
72
Progetto di Applicazioni Software
Anno accademico 2008-2009
public Object replace(Object original, Object target, Object owner)
throws HibernateException {
return deepCopy(original);
}
//metodi chiamati per usare il secondo livello di cache
//in questo corso ignoriamo il secondo livello di cache, pertanto
//questi metodi saranno sempre definiti come segue
public Object assemble(Serializable cached, Object owner)
throws HibernateException {
return cached;
}
public Serializable disassemble(Object value)
throws HibernateException {
return (Serializable) value;
}
}
A. Poggi
73
Progetto di Applicazioni Software
Anno accademico 2008-2009
Mappare proprietà che sono valori con
molteplicità massima diversa da 1
Quando si ha una proprietà P persistente di una classe di
dominio D che è una collezione omogenea (Set,
List,Map...) di valori gestiti da un’altra tabella TABP, allora
nel file di mapping di D si definisce un elemento apposito
Noi vedremo come mappare Set e List, si rimanda alla doc
per gli altri tipi di collezioni omogenee
A. Poggi
74
Progetto di Applicazioni Software
Anno accademico 2008-2009
Mappare un attributo di tipo Set
Si usa l’elemento set:
<set name="P" table="TABP">
<key column="IDD"/>
<element type="TypeP" column="ColumnP"/>
</set>
dove:
• IDD è l’attributo di TABP che identifica l’istanza di D per
la quale si definisce il mapping
• ColumnP è la colonna di TABP che contiene i valori della
collezione mappati
A. Poggi
75
Progetto di Applicazioni Software
Anno accademico 2008-2009
Mappare un attributo di tipo List
Si usa l’elemento list:
<list name="P" table="TABP">
<key column="IDD"/>
<list-index column="POSITION"/>
<element type="TypeP" column="ColumnP"/>
</list>
dove POSITION è l’attributo di TABP che contiene il valore
dell’indice della lista
A. Poggi
76
Progetto di Applicazioni Software
Anno accademico 2008-2009
Mappare proprietà che realizzano associazioni
In linea di principio, Hibernate permette di mappare tutte le
realizzazioni di associazioni in Java
• proprietà riferimento ad un’istanza di un’altra classe
persistente
• proprietà insieme di riferimenti ad istanze di un’altra
classe
• proprietà riferimento ad un’istanza di una classe
“TipoLink”
• proprietà insieme di riferimenti ad istanze di una classe
“TipoLink”
A. Poggi
77
Progetto di Applicazioni Software
Anno accademico 2008-2009
Proprietà riferimento ad un’istanza di un’altra
classe persistente
Consideriamo il caso di due classi di dominio A e B
persistenti legate da un’associazione R senza attributi, con
molteplicità 0..1 o 1..1, sulla quale solo A ha responsabilità
→ la realizzazione in Java della classe A porta ad una
proprietà P ropB che è un riferimento ad un’istanza della
classe B .
Indichiamo con TabA e TabB rispettivamente le tabelle su
cui sono mappate A e B .
A. Poggi
78
Progetto di Applicazioni Software
Anno accademico 2008-2009
Ci sono 2 casi:
• Caso 1: TabA ha accorpato la relazione ER
corrispondente a R
→ in TabA sarà definito un vincolo di foreign key verso
TabB
Si usa l’elemento one-to-one:
<one-to-one name="PropB" class="B"/>
A. Poggi
79
Progetto di Applicazioni Software
Anno accademico 2008-2009
• Caso 2: la relazione ER corrispondente a R ha portato
ad una tabella T abR
→ T abR ha due foreign key FKA, FKB verso le tabelle su
cui sono mappate A e B , rispettivamente
Si usa l’elemento join:
<join table="TabR">
<key column="FKA"/>
<many-to-one name="PropB" class="B" column="FKB"/>
</join>
A. Poggi
80
Progetto di Applicazioni Software
Anno accademico 2008-2009
Proprietà insieme di riferimenti ad istanze di
un’altra classe persistente
Consideriamo il caso di due classi di dominio A e B
persistenti legate da un’associazione R senza attributi, con
molteplicità 0..∗ o 1..∗, sulla quale solo A ha responsabilità
A. Poggi
81
Progetto di Applicazioni Software
Anno accademico 2008-2009
Anche in questo caso, due casi sono possibili:
• Caso 1: TabB ha accorpato la relazione ER
corrispondente a R
→ in TabB è definita una foreign key FKA verso TabA
Si usa l’elemento set:
<set table="TabB" optional="true">
<key column="FKA"/>
<one-to-many class="B"/>
</join>
N.B. In questo caso assumiamo che la chiave primaria
di TabB sia la chiave primaria di TabA - attenzione: se
la chiave primaria di TabB è contenuta nella chiave nella
chiave primaria di TabA, allora Hibernate permette ad
implementare il mapping
A. Poggi
82
Progetto di Applicazioni Software
Anno accademico 2008-2009
• Caso 2: la relazione ER corrispondente a R ha portato
ad una tabella T ABR
→ T abR ha due foreign key FKA, FKB verso le tabelle su
cui sono mappate A e B , rispettivamente
Si usa anche questa volta l’elemento join:
<join table="TabR" optional="true">
<key column="FKA"/>
<many-to-one name="PropB" class="B" column="FKB"/>
</join>
A. Poggi
83
Progetto di Applicazioni Software
Anno accademico 2008-2009
Cascading di oggetti
Quando una classe A ha responsabilità su di un’associazione
AB con B , Hibernate fornisce la possibilità di richiedere che
una qualsiasi operazione di persistenza su A sia transitiva
es. il salvataggio di un oggetto di A, implichi il salvataggio
dell’oggetto di B associato ad A
Si definisce uguale ad "all" l’attributo cascade
dell’elemento XML che definisce il mapping per
l’associazione per indicare ad Hibernate che tutte le
operazioni devono essere persistenti (altrimenti si usa il
nome dell’operazione, e.g. "save-update")
Attenzione: Nel caso del delete transitivo, bisogna fare
attenzione a non lasciare dei riferimenti ad oggetti appesi
(cf. corso di Progettazione del software)
A. Poggi
84
Progetto di Applicazioni Software
Anno accademico 2008-2009
Caso in cui si ha una classe di tipo
“TipoLink”
Siano A e B due classi di dominio persistenti legate da
un’associazione R con attributi, tali che:
• R ha un attributo
• e/o A e B hanno entrambe responsabilità su R
→ la realizzazione in Java porta ad una classe java
T ipoLinkAB che realizza il link tra le istanze di A e B , ed ad
una classe java che realizza A e ha ad una proprietà P ropB
che è un riferimento ad un’istanza della classe T ipoLinkAB
Idea: mappiamo anche la classe T ipoLinkAB con
l’accortezza di usare un identificatore composto sia
dall’identificatore di A che dall’identificatore B
A. Poggi
85
Progetto di Applicazioni Software
Anno accademico 2008-2009
Link mappati più volte
Quando si mappa più volte lo stesso link tra oggetti di
classi java, il mapping indica a Hibernate di effettuare la
stessa operazione di persistenza più volte
→ si possono ottenere delle violazioni di chiave!
Si sceglie un verso per il link, e si definisce uguale a "true"
l’attributo inverse del mapping che specifica come
mappare il link nell’altro verso.
Esempio: caso delle classi “TipoLink”
A. Poggi
86
Progetto di Applicazioni Software
Anno accademico 2008-2009
Mappare gerarchie
Supponiamo di avere una generalizzazione completa, tale
che due classi A e B specializzano G, rispettivamente
mappate sulle tabelle TabA, TabB e TabG, tali che TabA e
TabB abbiano entrambe una foreign key FKA e FKB verso
TabG
Si definisce il mapping per G, definendo come mappare,
oltre alle proprietà di G, anche le proprietà delle sue
sottoclassi A e B .
A. Poggi
87
Progetto di Applicazioni Software
Anno accademico 2008-2009
Si usa l’elemento joined-subclass:
<joined-subclass name="A" table="TabA">
<key column="FKA"/>
(...)
</joined-subclass>
A. Poggi
88
Progetto di Applicazioni Software
Anno accademico 2008-2009
Note importanti sul mapping di gerarchie
• Il mapping descritto si può usare SOLO nel caso in cui
la gerarchia sia completa
• All’interno di uno stesso file di mapping non si possono
usare due elementi joined-subclass e join allo stesso
livello
⇒ In molti casi di gerarchie, non si può usare Hibernate ma
si deve ricorrere all’approccio DAO
A. Poggi
89
Progetto di Applicazioni Software
Anno accademico 2008-2009
Il ciclo di vita degli oggetti
A. Poggi
90
Progetto di Applicazioni Software
Anno accademico 2008-2009
Il ciclo di vita degli oggetti persistenti
Un’applicazione orientata agli oggetti che fa uso di un
meccanismo di persistenza deve interagire con il servizio di
persistenza ogni volta che ha bisogno di propagare lo stato
di un oggetto in memoria sulla base di dati
→ lo stato di un oggetto che appartiene ad una classe
persistente cambia a seconda dell’operazione del servizio di
persistenza che viene invocata
A. Poggi
91
Progetto di Applicazioni Software
Anno accademico 2008-2009
Il ciclo di vita degli oggetti delle classi
persistenti
new
garbage
Transiente
save()
saveOrUpdate()
Rimosso
delete()
Persistente
evict()
close()
clear()
Staccato
A. Poggi
update()
saveOrUpdate()
merge()
garbage
92
Progetto di Applicazioni Software
Anno accademico 2008-2009
Lo stato transiente
• un oggetto creato con un’operazione new entra nello
stato transiente, in quanto non è (ancora) associato
con alcuna tupla della base di dati
• la vita di un oggetto nello stato transiente termina non
appena non esiste più alcun oggetto che fa riferimento
ad esso (diventa disponibile per il servizio di garbage
collection)
• Hibernate non fornisce alcun servizio di roll-back per gli
oggetti nello stato transiente
A. Poggi
93
Progetto di Applicazioni Software
Anno accademico 2008-2009
Lo stato persistente
• un oggetto nello stato persistente è un oggetto che ha
associato un identificatore proveniente dalla base di dati
• un oggetto nello stato persistente è sempre associato
ad un contesto di persistenza, ovvero ad un insieme di
oggetti dell’applicazione “sincronizzati” rispetto alla
base di dati
→ il contesto di persistenza può essere considerato
come una cache di istanze associata all’istanza sessione
(istanza di Session) correntemente aperta: quando la
sessione viene chiusa, la cache viene svuotata
A. Poggi
94
Progetto di Applicazioni Software
Anno accademico 2008-2009
Il contesto di persistenza
• il contesto di persistenza è utile ai seguenti fini:
– per fare automatic dirty checking: quando il
contesto di persistenza è chiuso, lo stato della cache
è propagato alla base di dati in maniera
transazionale
→ tutte e sole le proprietà degli oggetti persistenti
che sono state modificate saranno aggiornate
– per fare write-behind: modifiche agli oggetti nel
contesto di persistenza sono propagate alla base di
dati all’invocazione del metodo flush
→ ottimizza gli accessi e riduce la durata del lock
sul database
A. Poggi
95
Progetto di Applicazioni Software
Anno accademico 2008-2009
– per realizzare un raggio dell’applicazione all’interno
del quale gli oggetti coinvolti sono tra loro
consistenti, in modo tale da garantire (i) l’identità
degli oggetti (i.e., una tupla di valori della base di
dati è associata al più ad un oggetto), (ii) il
repeatable read
– per ottimizzare gli accessi, realizzando a tutti gli
effetti una cache di primo livello
A. Poggi
96
Progetto di Applicazioni Software
Anno accademico 2008-2009
Transazioni e Session
• All’apertura di un nuovo oggetto della classe Session
(metodo openSession() della classe SessionFactory)
viene creata una nuova connessione JDBC con la base
di dati
→ di default, Hibernate imposta l’autocommit a false,
il che comincia a tutti gli effetti una transazione JDBC!
A. Poggi
97
Progetto di Applicazioni Software
Anno accademico 2008-2009
• quando viene invocato il metodo flush le modifiche nel
contesto di persistenza sono sincronizzate con la base
di dati
Attenzione: non viene eseguito commit, quindi l’esito
della transazione dipende dal DBMS sottostante
⇒ è necessario usare la classe transaction, con i suoi
metodi commit sia rollback
N.B. Di default, il flushing della sessione avviene
implicitamente al quando viene invocato il commit e
prima di eseguire una query
A. Poggi
98
Progetto di Applicazioni Software
Anno accademico 2008-2009
Locking ottimistico di sessione
• in caso di accesso concorrente, affinché non occorrano
anomalie di tipo lost-update, Hibernate prevede la
possibilità di abilitare un meccanismo di locking
ottimistico tale che nel momento in cui viene invocato
il flushing, verifica se la porzione della base di dati che
sarà modificata non sia cambiata
→ se è cambiata lancia un’eccezione
StaleObjectStateException
• per abilitare il locking ottimistico, nell’elemento class
del file di mapping relativo alla classe su cui verte la
modifica, è necessario definire l’attributo
optimistic-lock pari ad all
A. Poggi
99
Progetto di Applicazioni Software
Anno accademico 2008-2009
Rendere un oggetto persistente
• un oggetto entra nello stato persistente:
– dopo che sull’oggetto nello stato transiente è
invocato uno dei metodi save/saveOrUpdate
Session
(Contesto di persistenza)
openSession()
new(O)
T
save(o)
saveOrUpdate(o)
P
N.B. Il metodo saveOrUpdate verifica prima se
l’oggetto occorre nella base di dati ed a seconda
dell’esito della verifica, effettua un’operazione di
INSERT o di UPDATE
A. Poggi
100
Progetto di Applicazioni Software
Anno accademico 2008-2009
– quando viene creato (i) invocando uno dei metodi
get e load del servizio di persistenza, (ii) come
risultato di una query, (iii) navigando il grafo degli
oggetti a partire da un’altra istanza persistente
Session
(Contesto di persistenza)
openSession()
new(O)
T
save(o)
saveOrUpdate(o)
P
N.B. I due metodi get e load si distinguono per il
fatto che se l’identificatore non viene trovato, get
restituisce null mentre load lancia una
ObjectNotFoundException
A. Poggi
101
Progetto di Applicazioni Software
Anno accademico 2008-2009
Lo stato staccato
• un oggetto è nello stato staccato se era nello stato
persistente ed è stato poi cancellato dal contesto di
persistenza
→ il riferimento all’oggetto rimane ma può:
– divenire obsoleto a seguito di cambiamenti sulla
base di dati
– non essere più sincronizzato con i valori ad esso
associati nella base di datiè, in quanto l’applicazione
stessa può modificare alcune sue proprietà
A. Poggi
102
Progetto di Applicazioni Software
Anno accademico 2008-2009
• un oggetto può essere reso staccato invocando uno tra
i metodi evict/ clear (che appunto cancellano
l’istanza/tutte le istanze dalla cache del contesto di
persistenza)
• alla chiusura della sessione, un oggetto entra nello
stato staccato
Session
(Contesto di persistenza)
openSession()
new(O)
A. Poggi
T
save(o)
saveOrUpdate(o)
flush()
P
close()
S
103
Progetto di Applicazioni Software
Anno accademico 2008-2009
• un oggetto nello stato staccato può tornare ad entrare
nello stato persistente attraverso l’invocazione dei
metodi update/saveOrUpdate
Session
(Contesto di persistenza)
openSession()
S
update(o)
saveOrUpdate(o)
flush()
P
close()
S
setValue(v)
A. Poggi
104
Progetto di Applicazioni Software
Anno accademico 2008-2009
• quando si vuole fare tornare ad essere persistente un
oggetto che è nello stato staccato ed esiste già
un’istanza persistente corrispondente alla stessa tupla
della base di dati, si invoca il metodo merge, che copia
le proprietà dell’oggetto staccato sulle proprietà
dell’oggetto persistente corrispondente alla stessa tupla
della base di dati
Session
(Contesto di persistenza)
openSession()
object o’
getid(id)
flush()
P
close()
S
(copy properties(o,o’))
object o
S
merge(o)
A. Poggi
105
Progetto di Applicazioni Software
Anno accademico 2008-2009
Lo stato rimosso
• un oggetto è nello stato rimosso se era nello stato
persistente ed è invocato il metodo delete
→ è stato schedulato per essere rimosso al termine di
un’unità di lavoro, ma fa ancora parte del contesto di
persistenza finché l’unità di lavoro non è completata
Session
(Contesto di persistenza)
openSession()
load(id)
A. Poggi
flush()
P
delete(o)
R
close()
S
106
Progetto di Applicazioni Software
Anno accademico 2008-2009
• un oggetto nello stato rimosso non deve essere
“riusato” e devono essere rimossi tutti i riferimenti ad
esso, in quanto sarà cancellato dalla base di dati
appena l’unità di lavoro sarà completata
• un oggetto nello stato rimosso diventa disponibile per il
servizio di garbage collection al termine dell’unità di
lavoro
A. Poggi
107
Progetto di Applicazioni Software
Anno accademico 2008-2009
Long-lived transactions
• richiamo: tra il momento in cui si leggono i dati dal DB
ed il momento in cui eventuali modifiche si rendono
persistenti può trascorrere molto tempo
• si applica il pattern session-per-conversation: è
necessario disabilitare il flushing automatico chiamando
il metodo setFlushMode(FlushMode.MANUAL) quando
la sessione è aperta
A. Poggi
108
Progetto di Applicazioni Software
Anno accademico 2008-2009
→ l’isolamento è garantito in quanto, abilitando il
locking ottimistico, le modifiche fatte in transazioni
concorrenti sono riconosciute
→ l’atomicità è garantita dal fatto che il flushing viene
eseguito solo al termine della transazione, e se la
sessione viene chiusa senza fare il flushing, la
transazione viene abortita
A. Poggi
109
Progetto di Applicazioni Software
Anno accademico 2008-2009
Nota sui servizi che accedono direttamente
alla base di dati
• come già visto nell’approccio DAO, a volte è necessario
accedere direttamente alla base di dati per mezzo di
query SQL (senza passare dagli oggetti delle classi
mappate)
• il principale vantaggio che offre Hibernate è la
conversione automatica di un result set in oggetti della
base di dati
A. Poggi
110
Progetto di Applicazioni Software
Anno accademico 2008-2009
List result = session.
createSQLQuery("SELECT * FROM CATEGORY").
addEntity(Category.Class).list();
→ Hibernate legge il result set e sulla base del file di
mapping definito per la classe Category, analizza le
colonne restituite: per ogni colonna restituita, verifica
se è mappata su di una proprietà della classe, e se lo è,
popola la suddetta proprietà e restituisce una lista di
oggetti caricati dalla base di dati
Attenzione: questo meccanismo funziona correttamente
solo se si usano delle query SQL “semplici” come
quella su specificata...
A. Poggi
111
Progetto di Applicazioni Software
Anno accademico 2008-2009
Realizzare un livello di
persistenza generico
A. Poggi
112
Progetto di Applicazioni Software
Anno accademico 2008-2009
Perché un livello di persistenza generico?
• permette di astrarre dalla tecnologia ORM usata e di
fornire un livello di astrazione maggiore, concentrato
sui servizi ad alto livello forniti dal livello di persistenza
• permette di nascondere al client l’implementazione
effettiva del livello di persistenza
• permette di far coesistere approcci misti all’ORM, i.e.
Hibernate e l’approccio DAO “hand-coded”
A. Poggi
113
Progetto di Applicazioni Software
Anno accademico 2008-2009
Pattern DAO con interfacciamento generico
Il pattern di programmazione che useremo prevede la
definzione di un’interfaccia che comprende tutte le
operazioni di persistenza di base (CRUD e metodi di
ricerca) necessarie per ogni classe persistente
A. Poggi
114
Progetto di Applicazioni Software
Anno accademico 2008-2009
GenericDAO<T,ID>
findById(ID id)
makePersistent()
makeTransient()
GenericDAOMio<T,ID>
GenericDAOHibernate<T,ID>
GenericItemDAO<Item,int>
CalcolaTempoResiduo()
GenericDAOHibernate<T,ID>
GenericDAOHibernate<T,ID>
GenericUserDAO<Item,int>
GenericDAOHibernate<T,ID>
GenericDAOHibernate<T,ID>
A. Poggi
115
Progetto di Applicazioni Software
Anno accademico 2008-2009
Generica super-interfaccia
Raggruppa tutte le operazioni CRUD di base condivise da
tutte le classi persistenti
public interface GenericDAO<T,ID extends Serializable> {
T findById(ID id);
T makePersistent(T class);
void makeTransient(T class);
}
Eventualmente, può comprendere i metodi flush e clear.
A. Poggi
116
Progetto di Applicazioni Software
Anno accademico 2008-2009
Generica implementazione con Hibernate
Una possibile implementazione della super-interfaccia
generica usa Hibernate, ovvero include proprietà specifiche
di un livello di persistenza realizzato con Hibernate
condivise da tutte le classi la cui persistenza è realizzata
con Hibernate
public abstract class
GenericDAOHibernate<T,ID extends Serializable>
implements GenericDAO<T, ID estends Serializable> {
private Class<T> persistentClass;
private Session session;
public GenericDAOHibernate(){
this.persistentClass= (Class<T>)
(ParameterizedType) getClass().getGenericSuperClass())
.getActualTypeArguments().[0];
}
A. Poggi
117
Progetto di Applicazioni Software
Anno accademico 2008-2009
public void setSession (Session s) {
this.session = s;
}
protected Session openSession(){
if (session==null)
session = HibernateUtil.getSessionFactory().
openSession();
return session;
}
protected Session getSession(){
if (session==null)
session = HibernateUtil.getSessionFactory().
getCurrentSession();
return session;
}
public Class<T> getPersistentClass(){
return persistentClass;
}
public T findById(ID is) {
T class = (T) getSession.load(getPersistentClass(),id);
A. Poggi
118
Progetto di Applicazioni Software
Anno accademico 2008-2009
return class;
}
public T makePersistent(T class) {
getSession.saveOrUpdate(class);
return class;
}
public void makeTransient(T class) {
getSession.delete(class);
}
public void flush() {
getSession.flush();
}
public void clear() {
getSession.clear();
}
}
A. Poggi
119
Progetto di Applicazioni Software
Anno accademico 2008-2009
Interfacciamento specifico delle classi
persistenti
Per ogni classe persistente, si definisce una sotto-interfaccia
che comprende i metodi specifici della classe
public interface ItemDAO
extends GenericDAO<Item,int> {
int CalcolaTempoResiduo();
}
A. Poggi
120
Progetto di Applicazioni Software
Anno accademico 2008-2009
Implementazione di classi persistenti con
Hibernate
Rimangono da definire le sottoclassi dell’implementazione
generica con Hibernate, che implementano l’interfaccia
specifica di ogni classe persistente
public class ItemDAOHibernate
extends GenericDAOHibernate<Item, int>
implements ItemDAO {
public int CalcolaTempoResiduo(){
(...)
}
}
A. Poggi
121
Progetto di Applicazioni Software
Anno accademico 2008-2009
Nota
Per realizzare un’implementazione che segue l’approccio
DAO “hand-coded” è necassario allo stesso modo, definire
un’implementazione generica e una sua sottoclasse per ogni
classe persistente
→ cf. lezione sull’approccio DAO all’ORM
A. Poggi
122
Progetto di Applicazioni Software
Anno accademico 2008-2009
Esempio di uso del livello di persistenza
Quando si deve creare un oggetto della classe persistente, è
necessario fare riferimento all’implementazione usata
ItemDAO itemDAO = new ItemDAOHibernate();
UserDAO userDAO =new UserDAOMio();
Item item = itemDAO.findById(itemId);
(...)
}
A. Poggi
123