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