15 Hibernate3 Le transazioni

Transcript

15 Hibernate3 Le transazioni
Hibernate
Le transazioni
Dott. Doria Mauro
[email protected]
[email protected]
Introduzione
La demarcazione delle transazioni può essere fatta:
–
–
Una applicazione può:
–
–
2
In maniera programmatica: demarcazione all’interno del codice
applicativo.
In maniera dichiarativa: demarcazione all’interno dei files di
configurazione.
Non avvalersi di un gestore transazionale: uso diretto di JDBC
anche per la gestione delle transazioni
Avvalersi di un gestore transazionale: interfacciamento con gestore
transazionale (lo standard in java è JTA)
L’interfaccia fondamentale di Hibernate per le transazioni è
org.hibernate.Transaction.
[email protected]
JDBC (Java DataBase Connectivity)
JDBC è lo standard Java per l’accesso ai database
la specifica JDBC si concretizza attraverso i drivers che
realizzano l’effettivo strato di codice di accesso per specifici
DBMS
JDBC fa uso diretto di SQL compreso le istruzioni per la
demarcazione delle transazioni.
La principale interfaccia di JDBC e java.sql.Connection; i metodi
per la demarcazione delle transazioni sono:
–
–
3
–
setAutoCommitt(boolean b)
committ()
rollBack()
[email protected]
JTA (Java Transaction API)
E’ uno standard della Java J2EE per la gestione delle transazioni
all’interno dello strato applicativo.
Gli scopi di JTA sono:
–
–
–
La principale interfaccia della parte di JTA rivolta allo
sviluppatore è javax.transaction.UserTransaction, i cui metodi per
la demarcazione sono:
–
4
Gestione delle transazioni di distribuite: singole transazioni su
diversi Database anche remoti (commit a due fasi)
Demarcazione standardizzata delle transazioni
Gestione delle risorse (ad esempio, connection pool)
–
begin(): marcare l’inizio di una transazione
committ(): marcare la fine di una transazione
[email protected]
Hibernate transaction
5
L’interaccia Transaction di Hibernate provvede a fornire gli
strumenti necessari con cui l’applicazione opera con le
transazioni.
L’interfaccia Transaction è indipendente dal tipo di gestore
transazionale (JDBC diretto o JTA) e si ottiene da una apposita
factory.
La scelta di quale gestore transazionale utilizzare viene effettuata
in fase di configurazione tramite la proprietà:
hibernate.transaction.factory_class=org.hibernate.transaction.JDBCTransactionFactory
oppure
hibernate.transaction.factory_classe=org.hibernate.transaction.JTATransactionFactory
hibernate.transaction.manager_lookup_class=“nome del servizio concreto JTA”
[email protected]
Hibernate transaction
6
Hibernate consente di aprire e chiudere transazioni all’interno di una
singola sessione.
Per
ottenere
questo,
è
necessario
invocare
i
metodi
sessionFactory.openSession() e session.close().
Session session = null;
Transaction tx, tx2 = null;
try {
session = sessionFactory.openSession();
tx = session.beginTransaction();
concludeAuction(session);
tx.commit();
tx2 = session.beginTransaction();
…..
tx2.commit();
session.close();
….
utilizzando invece il metodo
sessionFactory.getCurrentSession()
al posto di openSession() la
sessione si chiude
automaticamente alla prima
commit()
[email protected]
Transazioni con JDBC
Uso dell’ interfaccia Transaction in un ambiente non gestito
…
Si ottiene un oggetto JDBC
Session session = null;
Connection e viene invocato il
Transaction tx = null;
metodo setAutoCommit(false)
try {
session = sessionFactory.openSession();
tx = session.beginTransaction();
Invocazione del metodo commit()
concludeAuction(session);
(quindi flushing dei dati) e rilascio
tx.commit();
della connessione
} catch (RuntimeException ex) {
tx.rollback();
NOTA: invocando il metodo
} finally {
beginTransaction() si inizia
session.close();
una nuova transazione con
}
7
un nuovo oggetto Connection
[email protected]
Transazioni con JTA
Uso dell’ interfaccia Transaction in un ambiente gestito da JTA
…
Si ottiene un oggetto JDBC
Session session = null;
Connection dalla connection pool
Transaction tx = null;
gestita da JTA
try {
session = sessionFactory.openSession();
tx = session.beginTransaction();
La connessione viene acquisita e
concludeAuction(session);
restituita al pool per ogni
tx.commit();
istruzione SQL
} catch (RuntimeException ex) {
tx.rollback();
} finally {
session.close();
}
8
Invocazione del metodo commit()
(quindi flushing dei dati) e rilascio
della connessione
NOTA: il codice non cambia tra JDBC e JTA
[email protected]
Transazioni con JTA
9
Essendo JTA uno standard è possibile utilizzare le sue interfacce
direttamente nel codice insieme ad Hibernate:
UserTransaction utx = (UserTransaction) new InitialContext().
lookup("java:comp/UserTransaction");
Session session1 = null;
L’accesso alla UserTransaction si
Session session2 = null;
ottiene da JNDI
try {
utx.begin();
session1 = auctionDatabase.openSession(); Di default, bisogna manualmente
session2 = billingDatabase.openSession();
effettuare il flushing dei dati e la
concludeAuction(session1);
chiusura della sessione.
billAuction(session2);
session1.flush();
session2.flush();
utx.commit();
session1.close(); session2.close();
….
[email protected]
Transazioni con JTA
L’uso dell’interfaccia UserTransaction di JTA non inibisce le
operazioni di Hibernate che, ansi, collabora con essa.
L’ interazione tra i due dipende dal aver configurato le proprietà:
–
–
Hibernate gestirà il flushing dei dati e la chiusura delle
connessioni se si configurano anche i seguenti parametri:
–
–
10
hibernate.transaction.factory_class
hibernate.transaction.manager_lookup_class
hibernate.transaction.flush_before_completion
hibernate.transaction.auto_close_session
[email protected]
Livelli di isolation
Se nessun settaggio viene effettuato, JDBC ritorna un livello di
isolation di default (di solito il secondo o il terzo) che dipende dal
driver di connessione
Per settare il livello globale di isolation, è necessario aggiungere
una proprietà alla configurazione di Hibernate:
hibernate.connection.isolation=“livello di isolation”
Di seguito i quattro possibili valori:
–
–
–
11
–
1 Read uncommitted isolation
2 Read committed isolation
4 Repeatable read isolation
8 Serializable isolation
NOTA: Hibernate non può mai
modificare il livello di isolation
settato dal provaider del DB (ad
esempio JTA) in un ambiente
gestito.
[email protected]
Optimistic concurrency control
12
L’approccio ottimistico alla concorrenza presuppone che i conflitti
sui dati siano rari
E’ una tecnica per cui si sceglie un livello di isolation di base non
troppo restrittivo (visione ottimistica della concorrenza) per poi
aggiungere maggiori garanzie quando necessario.
Ad esempio, potremmo avere come livello di isolation base readcommitted con un controllo ottimistico della concorrenza.
Viene sollevata una eccezione soltanto al momento della
chiusura della unit work (non prima)
[email protected]
Controllo della concorrenza
In Hibernate è possibile attivare il controllo ottimistico della
concorrenza.
–
Nel caso non si attivi si adotta la gestione LAST COMMIT WINS
–
Nel caso si attivi si adotta la gestione FIRS LAST WINS
Cosa succede al punto 6?
13
[email protected]
Abilitare il versioning
14
Il controllo ottimistico della concorrenza ha bisogno di conoscere
la versione dei dati su cui si lavora
Hibernate offre una gestione automatica del versioning: ogni
classe entity ha un valore che rappresenta la versione; questo
valore verrà automaticamente incrementato, comparato per
riscontrare eventuali errori diversioning; Hibernate provvede,
inoltre, a sollevare una eccezione in caso di conflitto.
Il valore della versione può essere un numero o un timestamp
Lo sviluppatore deve aggiungere la proprietà alla classe ed
configurare opportunamente Hibernate.
[email protected]
Abilitare il versioning
Ad esempio, aggiungiamo una nuova proprietà alla classe Item:
public class Item {
...
private int version;
...
}
15
Questo campo è gestito da
Hibernate e l’applicazione non
deve modificarlo: proprietà private
senza metodi get/set.
Tipo int
Infine si aggiunge al file di mapping della classe Item:
<class name="Item" table="ITEM">
NOTA: il tag <version> deve
<id .../>
essere aggiunto subito dopo il
<version name="version"
tag <id>
access="field"
column="OBJ_VERSION"/>
...
</class>
[email protected]
Abilitare il versioning
Ad esempio, aggiungiamo una nuova proprietà alla classe Item:
public class Item {
...
private Date lastUpdated;
...
}
Questo campo è gestito da
Hibernate e l’applicazione non
deve modificarlo
Tipo per il timestamp
Infine si aggiunge al file di mapping della classe Item:
<class name="Item" table="ITEM">
<id .../>
<timestamp name="lastUpdated"
access="field"
column="LAST_UPDATED“
source=“db”/>
</class>
16
NOTA: il tag <timestamp> deve
essere aggiunto subito dopo il
tag <id>
Timestamp
recuperati
dalla
macchina server e non dalla
JVM
NOTA: la presenza di <version> o <timestamp> attiva la gestione ottimistica di Hibernate
[email protected]
Versioning con Timestamp
17
Gestire il versioning con i timestamps è leggermente meno sicuro
che con i numeri.
Per indicare la versione, i valori numerici si incrementano mentre
i timestamp si sostituiscono con valori recuperati dalla macchina.
Questo, in teoria, può portate ad un problema se una certa
operazione viene fatta contemporaneamente (in termini di
millisecondi)
Inoltre, recuperare un timestamp dalla JVM non è sempre una
operazione sicura negli ambienti distribuiti. Per questo è
possibile recuperare il timestamp dalla macchina server (ma non
tutti i dialetti supportano questa opzione)
[email protected]
Gestione del versioning
Di seguito le azioni che Hibernate compie nel gestire i conflitti. Facciamo
un esempio:
1.
2.
3.
4.
5.
In una prima unit work si carica un oggetto Item con numero di versione 1.
In una seconda unit work concorrente si carica lo stesso oggetto Item con
numero di versione sempre uguale a 1.
Nella prima unit work si invoca un metodo set sull’oggetto cambiandone lo
stato e si avvia il flushing dei dati.
Hibernate vede il cambio di stato e aumenta il numero di versione a 2.
Hibernate esegue la seguente UPDATE:
update ITEM set INITIAL_PRICE='12.99', OBJ_VERSION=2
where ITEM_ID=123 and OBJ_VERSION=1
6.
7.
18
Nella seconda unit work si invoca un metodo set sull’oggetto cambiandone
lo stato e si avvia il flushing dei dati.
Hibernate lancia la stessa istruzione UPDATE SQL e conteggia il numero di
righe coinvolte. Nel caso siano 0, solleva una StaleObjectStateException
all’applicazione che gestirà il problema (ad es. ricominciando l’operazione).
[email protected]
Incremento della versione
Hibernate aggiorna la versione dell’oggetto ogni volta che un
qualunque suo campo value viene modificato. I campi, quindi
possono essere:
–
–
–
Se, ad esempio, si modifica un campo ad un oggetto Item:
–
–
–
19
Singoli valori
Componenti
Collections
Se il campo è descrizione, la versione viene modificata
Se modifico un valore di una sua carta di credito, la versione NON
viene modificata
Se aggiungo o rimuovo una carta di credito la versione viene
modificata
NOTA: si può aggiungere l’attributo optimistic-lock="false“ nel mapping delle proprietà
[email protected]
Versioning senza valori
20
Nel caso il DB esista già e sia non modificabile, aggiungere una
proprietà alle classi per il versioning può risultare impossibile.
Hibenrate offre anche un meccanismo di versioning senza campi
aggiuntivi del tutto automatico
Per attivarlo è necessario aggiungere la proprietà optimistic-lock
al tag <class> nel file di mapping.
Questo meccanismo è in grado di individuare i conflitti soltanto nell’ambito
di un singolo persistence contex. Si sconsiglia l’utilizzo quando non
strettamente necessario
[email protected]
Livelli di isolation selettivi
21
E spesso inutile e dannoso modificare il livello di isolation
dell’intero DB soltanto a causa di alcune transazioni.
E’ possibile modificare il livello di isolation soltanto per quelle
transazioni che richiedono una speciale attenzione.
Session session = sessionFactory.openSession();
Modifica della politica dei lock
Transaction tx = session.beginTransaction();
sull’oggetto i nella transazione
Item i = (Item) session.get(Item.class, 123);
corrente
session.lock(i, LockMode.UPGRADE);
String description = (String) session.createQuery("select i.description from Item i" +
" where i.id = :itemid")
.setParameter("itemid", i.getId() )
.uniqueResult();
tx.commit();
session.close();
[email protected]
I tipi di lock in Hibernate
I tipi di lock in Hibernate sono:
–
–
–
–
–
–
22
LockMode.NONE: non accede al DB a meno che l’oggetto non è
nella cache
LockMode.READ: salta la cache ed accede al DB per verificare che
l’oggetto in memoria sia della stessa versione di quello sul DB
LockMode.UPDGRADE: salta la cache, fa un controllo delle versioni
e aumenta il livello dei lock. Se il dialetto non lo supporta diventa
LockMode.READ.
LockMode.UPDGRADE_NOWAIT: come LockMode.UPDGRADE
ma implementata con una select di tipo NOWAIT
LockMode.FORCE: forza l’incremento della versione nel DB
LockMode.WRITE: quando Hibernate effettua una scrittura nella
transazione corrente
[email protected]
Domande?
Transaction
Versioning
jta
JDBC
Optimistic
Livelli di isolation
replicate
23
save
Concorrenza