Post on 09-Apr-2020
transcript
Laboratorio Hibernate
Hibernate Laboratorio
Programmazione di Applicazioni Data Intensive
Laurea in Ingegneria e Scienze Informatiche DISI – Università di Bologna, Cesena
Giacomo Domeniconi, Gianluca Moro, Roberto Pasolini DISI Università di Bologna, Cesena
name.surname@unibo.it
Laboratorio Hibernate
Riepilogo: Object-Relational Mapping
• Molte applicazioni sviluppate secondo il paradigma Object-Oriented gestiscono dati persistenti su database relazionali
• Object-Relational Mapping indica la soluzione per salvare e reperire oggetti su database relazionale, che risolve le differenze tra i due paradigmi (impedance mismatch) – identità dei dati, tipi di dato, navigabilità, …
• In genere a ciascuna classe model corrisponde una tabella sul DB e a ciascun oggetto persistente corrisponde una riga – Ad ogni proprietà di un oggetto bean corrisponde una colonna (o più)
– Le associazioni tra oggetti persistenti richiedono particolare attenzione
• Sono stati considerati tre approcci generali all’ORM – Il più semplice è integrare l’ORM in model e/o controller (forza bruta)
Applicazioni Data Intensive 2
Laboratorio Hibernate
Riepilogo: Data Access Object
• L’approccio DAO all’ORM prevede di incapsulare la logica di accesso al database in appositi Data Access Object – Il resto dell’applicazione usa i DAO per gestire oggetti persistenti
– Cambiamenti al database richiedono cambiamenti solo ai DAO
• I DAO forniscono metodi di alto livello per gestire i dati – Operazioni elementari su singoli oggetti (Create, Read, Update, Delete)
e altre (es. reperire tutte le categorie) a seconda delle necessità
• Per implementare i DAO si possono usare librerie per l’accesso diretto al database come JDBC – È in genere necessario specificare molte operazioni di basso livello,
secondo schemi ricorrenti per ciascun tipo di azione di alto livello
Applicazioni Data Intensive 3
Laboratorio Hibernate
Riepilogo: framework per la persistenza
• L’implementazione dei DAO basata su JDBC richiede grandi quantità di codice – Nella pratica ciò comporta tempi di sviluppo prolungati e alto rischio di
errori, nonostante le implementazioni seguano schemi ricorrenti
• I framework per la persistenza forniscono implementazioni generali e riusabili delle tipiche operazioni dei DAO – Gli sviluppatori non devono più implementare tutta la logica di accesso
al DB, ma solamente configurare il framework ed utilizzarne l’API
• Hibernate è un framework di persistenza in Java molto usato – implementazione open source dello standard JPA
Applicazioni Data Intensive 4
Laboratorio Hibernate
Caso di studio: DAO basato su Hibernate
• La nostra webapp definisce un’interfaccia generica GeneralDAO per l’accesso al DB
• Nella prima versione, questa era implementata in JDBC – Classe JDBCGeneralDAO in EStore-web.zip
• In questa esercitazione lavoreremo invece su un’implementazione del DAO basata su Hibernate – dichiarazione dei mapping entità-tabelle tramite annotazioni
– uso API di Hibernate (al posto di JDBC)
• L’interfaccia del DAO rimane valida → i controller dell’applicazione non sono cambiati dalla prima versione
Applicazioni Data Intensive 5
Laboratorio Hibernate
Laboratorio: progetto basato su Hibernate
• Si scarichi il file progetto EStore-Hibernate.zip – Se rimasto, eliminare dallo spazio di lavoro Eclipse la versione
precedente del progetto (clic destro sul progetto > Delete)
– Importare quindi il nuovo progetto (come fatto per il precedente)
• Hibernate è in parte già configurato per l’applicazione – Il file di configurazione hibernate.cfg.xml è completo
– La nuova versione del DAO è abbozzata in HibernateGeneralDAO (package it.unibo.dia.estore.dao.hibernate)
– La gestione di sessioni e transazioni in ciascuna richiesta è già implementata tramite il filtro HibernateRequestFilter
• La configurazione però non è completa: al momento non possiamo eseguire la webapp (si avrebbe un’eccezione)
Applicazioni Data Intensive 6
Laboratorio Hibernate
Riepilogo: sessioni e transazioni in Hibernate
• Una Configuration di Hibernate definisce il mapping tra classi e tabelle e altri aspetti (URL database, dialetto SQL, …)
• Da essa si crea una SessionFactory unica per tutta l’app
• Da questa si possono aprire sessioni contemporanee, ciascuna rappresenta un’unità di lavoro (operazioni correlate tra loro)
• Session fornisce i metodi per gestire oggetti persistenti
• In una sessione possono svolgersi più transazioni in sequenza
• In una webapp è comune aprire una sessione apposita (e una transazione) per ogni richiesta (pattern session-per-request) – Nella nostra webapp ciò è gestito automaticamente dal filtro servlet HibernateRequestFilter
Applicazioni Data Intensive 7
Laboratorio Hibernate
Riepilogo: dichiarazione del mapping tra classi e tabelle
• Per dichiarare il mapping tra classi e tabelle del DB, usiamo annotazioni JPA sulle classi e sui metodi get delle proprietà
• @Entity dichiara una classe entità – @Table(name = "…") indica il nome della tabella corrispondente
• Ogni proprietà della classe entità è persistente di default – @Column indica nome e altre proprietà della colonna (es. vincoli)
– @Transient marca le proprietà non persistenti
• @Id marca la proprietà corrispondente alla chiave primaria – @GeneratedValue indica come sono generate le chiavi
• @Embedded indica un oggetto incorporato – La classe dell’oggetto deve essere marcata @Embeddable
Applicazioni Data Intensive 8
Laboratorio Hibernate
Riepilogo: dichiarazione associazioni tra classi
• Un associazione sussiste quando una classe entità A ha una proprietà il cui tipo è un’altra entità B (o una collezione)
• Se bidirezionale, anche B dichiara una proprietà di tipo A – Bisogna distinguere lato diretto e inverso: modifiche alle associazioni
tra oggetti vanno effettuate sul lato diretto
– Il mapping va configurato sul lato diretto dell’associazione, mentre su quello inverso si inserisce un riferimento al diretto con mappedBy
• @ManyToOne e @OneToOne marcano proprietà il cui valore è un altro singolo oggetto persistente – @JoinColumn indica la colonna che ne contiene la chiave esterna
• @OneToMany e @ManyToMany marcano proprietà il cui valore è una collezione di altri oggetti persistenti – @JoinTable indica tabella di join e colonne con le chiavi dei due lati
Applicazioni Data Intensive 9
Laboratorio Hibernate
Esercizio 1: mapping tra classe e tabella
• Dichiarare nella classe User del model il mapping alla corrispondente tabella del database
• Annotare la classe come entità indicando il nome della tabella
• Indicare la proprietà id come identificatore degli oggetti, corrispondente alla colonna oid
• Specificare il mapping sulle altre proprietà, indicando il nome della colonna dove necessario – proprietà name → colonna username
– proprietà password → colonna password
– proprietà mailAddress → colonna email
• Mappare la proprietà orders come inversa di user in invoice, restituire gli ordini ordinati per data decrescente
Applicazioni Data Intensive 10
Laboratorio Hibernate
Riepilogo: lettura degli oggetti dal database
• L’interfaccia di Session fornisce vari metodi per reperire oggetti dal database – reperimento singoli oggetti per tipo e chiave primaria
– interrogazione tramite query HQL, API Criteria e query native SQL
• Per il reperimento di oggetti dall’ID sono dati due metodi, entrambi hanno per argomenti classe entità e ID – get recupera effettivamente l’oggetto dal database, verificando che
esista e restituendo null altrimenti
– load restituisce un riferimento ad un oggetto senza accedere al database (quindi senza verificare che esista)
Applicazioni Data Intensive 11
Laboratorio Hibernate
Esercizio 2: metodi DAO per reperire oggetti data la chiave primaria
• Ora che Il mapping delle classi alle tabelle è completo, usiamo l’API di Hibernate per reperire e salvare oggetti sul database
• Le API sono usate nella classe HibernateGeneralDAO, che implementa i metodi definiti in GeneralDAO
• Le implementazioni usano la Session disponibile nel campo privato hb (aperta automaticamente per ogni richiesta)
• Implementare tutti i metodi getEntità(int) che restituiscono un oggetto dal database dato l’ID
• Usare il metodo get per reperire gli oggetti e restituirne il risultato (anche se null)
Applicazioni Data Intensive 12
Laboratorio Hibernate
Riepilogo: Hibernate Query Language
• HQL è usato per esprimere query sugli oggetti salvati nel DB
• La sintassi riprende quella di SQL – elementi di base: [ SELECT proprietà ] FROM entità [ WHERE condizione ] [ ORDER BY proprietà ]
– solo FROM è obbligatorio (da solo restituisce tutti gli oggetti di un tipo)
• Da Session si può ottenere un oggetto Query – Query query = s.createQuery( "FROM User" );
• La query può contenere parametri di cui dichiarare i valori – s.createQuery( "FROM Invoice WHERE user=:who" ) .setParameter( "who", someUser );
• Su un oggetto Query si può invocare – list per ottenere una lista di risultati (in forma di Object)
– uniqueResult per ottenere direttamente un unico risultato atteso
Applicazioni Data Intensive 13
Laboratorio Hibernate
Esercizio 3: metodi DAO di interrogazione
• Implementare i metodi di lettura dati rimanenti nel DAO utilizzando opportune query HQL
• getUserByName restituisce l’utente col nome dato (o null se non esiste)
• getCategoriesTree restituisce gli oggetti Category di primo livello nella gerarchia delle categorie, ordinati per nome – Sono restituite le categorie senza parent, che contengono
transitivamente riferimenti a tutte le altre categorie
• searchProducts restituisce i prodotti il cui nome contiene la stringa di ricerca data, ordinati per nome – In SQL/HQL: x contiene “abc” ↔ x LIKE '%abc%‘
• Una volta implementati questi metodi, si può avviare ed usare la webapp, almeno dove non è prevista scrittura di dati su DB
14 Applicazioni Data Intensive
Laboratorio Hibernate
Riepilogo: Gestione oggetti persistenti
• Gli oggetti si gestiscono tramite i metodi di Session
• Ogni oggetto di una classe entità ha tre possibili stati – transitorio: non gestito da Hibernate, non corrisponde ad una riga DB
– persistente: gestito da una sessione, corrisponde ad una riga sul DB
– scollegato: precedentemente gestito da una sessione poi chiusa
• Un oggetto persistente si può modicare con i metodi set – Se scollegato, va associato a nuova sessione con update o merge
• Un oggetto transitorio si salva nel DB con persist o save – save genera e restituisce l’identificatore assegnato all’oggetto
• Con delete si elimina un oggetto dal DB
• I cambiamenti si scaricano dalla cache sul DB con flush – Di default flush è eseguito in automatico al commit delle transazioni
Applicazioni Data Intensive 15
Laboratorio Hibernate
Esercizio 4: metodi DAO di scrittura
• Implementare il metodo insertInvoice che salva un nuovo ordine nel database e restituisce l’ID assegnatoli – I dettagli dell’ordine (oggetti InvoiceEntry) vanno aggiunti alla
collezione entries dell’oggetto Invoice: sono salvati in cascata
• Implementare il metodo updateReview che aggiorna la recensione (Review) associata ad una InvoiceEntry con ID dato – Recuperare un riferimento alla InvoiceEntry e aggiornarla
• Non è necessario usare transazioni e/o eseguire il flush della sessione: è tutto gestito da HibernateRequestFilter
• Dopo aver implementato questi metodi, l’intera webapp dovrebbe essere funzionante
Applicazioni Data Intensive 16
Laboratorio Hibernate
Riepilogo: Paginazione di grandi collezioni
• Nelle webapp, è comune dividere in pagine di dimensione fissa liste di elementi (es. prodotti) potenzialmente grandi – Reperire l’intera lista sarebbe gravoso per server, rete e client
– Ogni pagina spesso presenta link alle altre e indica il totale di elementi
• Sulle Query Hibernate si possono limitare i dati da reperire – setFirstResult taglia i primi n oggetti dai risultati
– setMaxResults imposta il numero massimo di oggetti da reperire
– Fissati N oggetti/pagina, la pagina p di risultati di una query q si ha con q.setMaxResults(N).setFirstResult(N*p).list()
• Queste funzionalità hanno limiti, che richiedono accorgimenti – disponibili solo su query, non su liste di oggetti incorporati/associati
– la lista da dividere in pagine deve avere un ordine fisso
– il numero totale di oggetti non è dato, serve una query aggiuntiva
Applicazioni Data Intensive 17
Laboratorio Hibernate
Supporto alla paginazione nella webapp
• La paginazione è utile in diversi contesti nella nostra webapp – risultati ricerca prodotti, prodotti per categoria, recensioni di prodotto
• Usiamo quindi un supporto generico valido in ogni contesto
• La classe ListPage incapsula una pagina di elementi e le informazioni su quantità di pagine e di elementi
• Il tag JSTL personalizzato pageinfo mostra informazioni e link di navigazione tra pagine per un oggetto ListPage dato – url indica l’URL tipo da usare per navigare tra le pagine
• ListPage definisce un costruttore con parametri: – List degli elementi che compongono la pagina rappresentata
– numero della pagina (contando da 0)
– numero di elementi per pagina
– numero totale di elementi nella collezione
Applicazioni Data Intensive 18
Laboratorio Hibernate
Esercizio 5: reperimento di dati in pagine
• Modificare il metodo searchProductsPaged in modo che restituisca un oggetto ListPage rappresentante un’unica pagina di prodotti risultanti da una ricerca – La versione attuale usa il metodo searchProducts per ottenere
tutti i risultati e li restituisce come una pagina unica
• Il metodo accetta come parametri: – la stringa di ricerca (query, come in searchProducts)
– il numero di elementi per pagina (itemsPerPage)
– il numero della pagina da reperire, contando da 0 (page)
• Testare il metodo su ricerche con molti risultati, ad es. “Star” – il numero di risultati per pagina si può cambiare in SearchServlet
Applicazioni Data Intensive 19