Hibernate Le transazioni Dott. Doria Mauro doriamauro@gmail.com
Introduzione La demarcazione delle transazioni può essere fatta: In maniera programmatica: demarcazione all interno del codice applicativo. In maniera dichiarativa: demarcazione all interno dei files di configurazione. Una applicazione può: 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) 2 L interfaccia fondamentale di Hibernate per le transazioni è org.hibernate.transaction.
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. 3 La principale interfaccia di JDBC e java.sql.connection; i metodi per la demarcazione delle transazioni sono: setautocommitt(boolean b) committ() rollback()
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: 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) 4 La principale interfaccia della parte di JTA rivolta allo sviluppatore è javax.transaction.usertransaction, i cui metodi per la demarcazione sono: begin(): marcare l inizio di una transazione committ(): marcare la fine di una transazione
Hibernate transaction 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à: 5 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
Hibernate transaction 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(). 6 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()
Transazioni con JDBC Uso dell interfaccia Transaction in un ambiente non gestito 7 Session session = null; Transaction tx = null; try { session = sessionfactory.opensession(); tx = session.begintransaction(); concludeauction(session); tx.commit(); } catch (RuntimeException ex) { tx.rollback(); } finally { session.close(); } Si ottiene un oggetto JDBC Connection e viene invocato il metodo setautocommit(false) Invocazione del metodo commit() (quindi flushing dei dati) e rilascio della connessione NOTA: invocando il metodo begintransaction() si inizia una nuova transazione con un nuovo oggetto Connection
Transazioni con JTA Uso dell interfaccia Transaction in un ambiente gestito da JTA Session session = null; Transaction tx = null; try { session = sessionfactory.opensession(); tx = session.begintransaction(); concludeauction(session); tx.commit(); Si ottiene un oggetto JDBC Connection dalla connection pool gestita da JTA La connessione viene acquisita e restituita al pool per ogni istruzione SQL 8 } catch (RuntimeException ex) { tx.rollback(); } finally { session.close(); } NOTA: il codice non cambia tra JDBC e JTA Invocazione del metodo commit() (quindi flushing dei dati) e rilascio della connessione
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; Session session2 = null; try { utx.begin(); session1 = auctiondatabase.opensession(); session2 = billingdatabase.opensession(); concludeauction(session1); billauction(session2); session1.flush(); session2.flush(); utx.commit(); session1.close(); session2.close();. L accesso alla UserTransaction si ottiene da JNDI Di default, bisogna manualmente effettuare il flushing dei dati e la chiusura della sessione.
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.transaction.factory_class hibernate.transaction.manager_lookup_class Hibernate gestirà il flushing dei dati e la chiusura delle connessioni se si configurano anche i seguenti parametri: hibernate.transaction.flush_before_completion hibernate.transaction.auto_close_session 10
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 11 Di seguito i quattro possibili valori: 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.
Optimistic concurrency control 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) 12
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
Abilitare il versioning 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 14 Lo sviluppatore deve aggiungere la proprietà alla classe ed configurare opportunamente Hibernate.
Abilitare il versioning Ad esempio, aggiungiamo una nuova proprietà alla classe Item: public class Item {... private int version;... } 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: 15 <class name="item" table="item"> <id.../> NOTA: il tag <version> deve <version name="version" essere aggiunto subito dopo il access="field" tag <id> column="obj_version"/>... </class>
Abilitare il versioning 16 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 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> Tipo per il timestamp 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
Versioning con Timestamp 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) 17 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)
Gestione del versioning Di seguito le azioni che Hibernate compie nel gestire i conflitti. Facciamo un esempio: 1. In una prima unit work si carica un oggetto Item con numero di versione 1. 2. In una seconda unit work concorrente si carica lo stesso oggetto Item con numero di versione sempre uguale a 1. 3. Nella prima unit work si invoca un metodo set sull oggetto cambiandone lo stato e si avvia il flushing dei dati. 4. Hibernate vede il cambio di stato e aumenta il numero di versione a 2. 5. Hibernate esegue la seguente UPDATE: update ITEM set INITIAL_PRICE='12.99', OBJ_VERSION=2 where ITEM_ID=123 and OBJ_VERSION=1 18 6. Nella seconda unit work si invoca un metodo set sull oggetto cambiandone lo stato e si avvia il flushing dei dati. 7. 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).
Incremento della versione Hibernate aggiorna la versione dell oggetto ogni volta che un qualunque suo campo value viene modificato. I campi, quindi possono essere: Singoli valori Componenti Collections 19 Se, ad esempio, si modifica un campo ad un oggetto Item: 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à
Versioning senza valori 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. 20 Questo meccanismo è in grado di individuare i conflitti soltanto nell ambito di un singolo persistence contex. Si sconsiglia l utilizzo quando non strettamente necessario
Livelli di isolation selettivi E spesso inutile e dannoso modificare il livello di isolation dell intero DB soltanto a causa di alcune transazioni. 21 E possibile modificare il livello di isolation soltanto per quelle transazioni che richiedono una speciale attenzione. Session session = sessionfactory.opensession(); Transaction tx = session.begintransaction(); Item i = (Item) session.get(item.class, 123); session.lock(i, LockMode.UPGRADE); Modifica della politica dei lock sull oggetto i nella transazione corrente String description = (String) session.createquery("select i.description from Item i" + " where i.id = :itemid").setparameter("itemid", i.getid() ).uniqueresult(); tx.commit(); session.close();
I tipi di lock in Hibernate 22 I tipi di lock in Hibernate sono: 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
Domande? Versioning Transaction jta Optimistic replicate JDBC Livelli di isolation 23 save Concorrenza