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