Hibernate Mapping delle associazioni Dott. Doria Mauro [email protected] [email protected] Mapping delle associazioni Abbiamo visto come vi sia un mapping dedicato per le associazioni tra classi entity e classi value. Hibernate ha un mapping specifico per le associazioni tra classi entity. Gli oggetti entity, avendo un ciclo di vita proprio e potendo essere condivisi, provedono un mapping più complesso. Considereremo l’esempio: Item 2 0..* Bid [email protected] Mapping delle associazioni NOTA: A differenza degli EJB 2.1, Hibernate non prevede un gestore delle associazioni bidirezionali. Questo vuol dire che se si aggiunge un Bid ad un Item aBid.setItem(anItem) Hibernate NON aggiunge automaticamente l’oggetto Bid all’ insieme degli item presente nella classe Item anItem.getBids().add(aBid) 3 Hibernate considera l’associazione Tra Bid e Item e l’associazione tra Item e Bid come due associazioni separate (e così dovremmo fare anche noi). [email protected] Framework e container Questo implica una fondamentale considerazione: Il framework Hibernate non ha un container 4 Una gestione automatica delle associazioni richiederebbe la presenza di un container sul framework Hibernate; Hibernate non costruisce oggetti entity e quindi non ne controlla il ciclo di vita; le operazioni sul framework invece avvengono attraverso i metodi di persistenza. Esattamente il contrario avviene su EJB 2.1 dove gli oggetti entity sono gestiti da un container (CMP significa appunto persistenza gestita dal container). Su EJB 2.1 le conseguenze di questa caratteristica portano a situazioni molto criticate. [email protected] Associazioni in Hibernate Le associazioni rappresentano il legame logico tra classi del modello e possono essere: – – – 5 Uno-a-uno Uno-a-molti (che è equivalmente a molti-a-uno) Molti-a-molti Inoltre, è possibile rappresentare associazioni unidirezionali o bidirezionali L’unidirezionalità o la bidirezionalità è prima di tutto una scelta progettuale. NOTA: bisogna tenere ben presente che nel modello relazionale del DB, esistono soltanto relazioni unidirezionali. [email protected] Associazione tra class Indipendentemente da Hibernate, le associazioni bidirezionali sono una caratteristica del modello delle classi. Item List<Bid> l get/setBids() Bid 1..* Item i get/setItem() NOTA: il rombo bianco significa che un oggetto bid non è legato indissolubilmente a un oggetto item; la mancanza della freccia denota la bidirezionalità della associazione. Nel nostro esempio, se si vuole aggiungere un Bid ad un item, si deve necessariamente agire da entrambi i “lati” dell’associazione unBid.addItem(unItem); unItem.getBids().add(unBid); 6 è prima di tutto una questione di references: se si esegue soltanto la seconda istruzione, la proprietà i (che è un puntatore) della classe Bid rimarrebbe a null. [email protected] Associazione molti-a-uno Come primo caso, consideriamo la classica associazione : Item 1..* Bid Alla classe Item aggiungiamo una Collection di oggetti Bid Se vogliamo una associazione bidirezionale, alla classe Bid aggiungiamo una proprietà di tipo Item NOTA: la bidirezionalità è interscambiabile tra le due classi 7 [email protected] Associazione molti-a-uno unidirezionale La classe e il mapping per l’associazione molti-a-uno: public class Bid { <class name="Bid“ table="BID"> ... ... private Item item; <many-to-one public void setItem(Item item) { name="item" this.item = item; column="ITEM_ID" } class="Item" public Item getItem() { not-null="true"/> return item; </class> } ….. ... } Opzionale. Hibernate Il campo ITEM_ID è determina da solo il posto not-null (abbiamo Associazione unidirezionale tipo della classe una associazione uno-aassociata molti e non zero-a-molti) 8 [email protected] Associazione molti-a-uno unidirezionale Le tabelle sono: Nel modello relazionale, le relazioni sono tutte bidirezionali. 9 E’ importante comprendere che l’associazione unidirezionale è sufficiente. Potremmo scrivere sono associazioni unidirezionali (tranne forse nel caso uno-a-uno). Ad esempio, si possono scrivere querys per ottenere tutti i bid di un item senza ricorrere ad una associazione bidirezionale. [email protected] Associazione molti-a-uno bidirezionale 10 Uno dei motivi per cui si utilizza un ORM framework come Hibenrate, è, però, quello di non dover scrivere querys ma agire sugli oggetti. Aggiungiamo la bidirezionalità: public class Item { ... private Set bids = new HashSet(); public void setBids(Set bids) { this.bids = bids; } public Set getBids() { return bids; } public void addBid(Bid bid) { bid.setItem(this); bids.add(bid); } ...} <class name="Item“ table="ITEM"> ... Manca not-null <set name="bids"> <key column="ITEM_ID"/> <one-to-many class="Bid"/> </set> </class> E’ inutile indicare il nome della tabella; è stata già indicata della configurazione della classe Bid NOTA: il tag <one-to-many> informa Hibernate che la collection è composta da references a oggetti entity e non da oggetti value. [email protected] Associazione molti-a-uno bidirezionale Ricordandosi che il legame tra Item e Bid è rappresentato da: – – – Come fa Hibernate a gestire le associazioni considerato che esiste un’unica colonna chiave esterna (ITEM_ID) a cui entrambe le associazioni fanno riferimento? Ricordarsi, inoltre, che Hibernate mantiene in memoria a run-time due diverse rappresentazioni dell’associazione bidirezionale: – – 11 una relazione uno(item)-a-molti(bid) nel modello ad oggetti una relazione molti(bid)-a-uno(item) nel modello ad oggetti Una unica associazione bidirezionale tra item e bid del modello relazionale Una collecion di bid nella classe Item Una singolo item nella classe bid [email protected] Associazione molti-a-uno bidirezionale Questo implica, nel nostro esempio che se scrivo bid.setItem(item); bids.add(bid); Le rappresentazioni in memoria delle associazioni vengono modificate entrambe e anche sul db abbiamo 2 modifiche uguali alla colonna ITEM_ID Per avere una vera bidirezionalità è necessario aggiungere l’attributo inverse. Tale attributo va aggiunto nella classe Item nel tag <set>. Attenzione: la posizione dell’attributo inverse conta! <set name="bids" inverse="true"> 12 Hibenrate basa la sua logica di persistenza dalla posizione dell’attributo inverse. [email protected] Associazione molti-a-uno bidirezionale Attenzione: Avendo collocato l’attributo inverse nella classe Item, se si effettuano modifiche attraverso la classe Item, Hibernate non le rende persistenti! Quindi, ad esempio, aggiungere un nuovo bid alla collection in Item non ha effetti sul DB!! Mentre, settando un Item su un oggetto bid, Hibernate prendere in carico l’operazione rendendola persistente. anItem.getBids().add(bid) NO DB aBid.setItem(anItem) SI DB In definitiva con inverse=“true”, 13 solo lo stato degli oggetti Bid è condierato da Hibernate per la sincronizzazione sul DB!!! [email protected] Associazione molti-a-uno bidirezionale NOTA: E’ consigliabile scrivere dei metodi appositi che gestiscono in maniera consistente le associazioni bidirezionali. Nel nostro esempio, la classe Item ha il metodo: public void addBid(Bid bid) { bid.setItem(this); bids.add(bid); } 14 [email protected] Associazione molti-a-uno bidirezionale Nell’ invocare il metodo addBid(), cosa abbiamo reso persistente? Ad esempio, eseguendo il seguente codice, cosa è persistente? Item newItem = new Item(); Bid newBid = new Bid(); newItem.addBid(newBid); //set di entrambi i lati dell’associazione In questo caso, gli oggetti newItem e newBid sono appena costruiti e Hibernate non sa ancora della propria esistenza (Hibernate non ha un container!!). E’ necessario, quindi rendere persistenti questi due oggetti: session.save(newItem); session.save(newBid); 15 A prima vista, la seconda istruzione sembrerebbe superflua ma non è così! Gli oggetti Bid hanno un ciclo di vita separato in quanto la classe Bid è di tipo entity; quindi newBid va reso persistente! [email protected] Associazione molti-a-uno transitive persistence: cascade=“save-update” 16 Si può procedere in tre possibili modi: – Gestione manuale: lo sviluppatore si prende carico della gestione della persistenza di entrambi gli oggetti invocando i metodi save() o delete() all’occorrenza oltre al normale codice di gestione della associazione (come aggiungere o rimuovere bid dalla collezione). – Rivisitazione del modello dei dati: La classe Bid potrebbe essere rivista come classe di tipo value. La persistenza degli oggetti Bid sarebbe a carico di Hibernate ma si perderebbero le caratteristiche degli entity (come la condivisione tra più oggetti) – Attivazione della gestione transitiva della persistenza: Hibernate gestisce il ciclo di vita degli oggetti associati automaticamente [email protected] Associazione molti-a-uno transitive persistence: cascade=“save-update” 17 Lo scopo è quello di mantenere un modello dei dati indipendente da Hibernate ma nello stesso tempo effettuare soltanto le operazioni di persistenza strettamente necessarie. Hibernate possiede un meccanismo che attiva la persistenza a cascata Nel nostro esempio, un nuovo oggetto Bid deve essere reso persistente automaticamente insieme all’oggetto Item. <class name="Item“ table="ITEM"> ... <set name="bids" inverse="true" cascade="save-update"> <key column="ITEM_ID"/> <one-to-many class="Bid"/> </set> </class> Save-update attiva la persistenza transitiva su oggetti Bid associati ad oggetti Item (nella collection). NOTA: l’opzione cascade può essere applicata anche nella classe Bid. In questo caso però, un Bid viene costruito sempre dopo un Item e quindi non avrebbe senso. [email protected] Associazione molti-a-uno transitive persistence: cascade=“save-update, delete” Cosa fare in caso di cancellazione di oggetti Bid? Al momento, se si vogliono eliminare tutti i bid di un item, bisogna procedere manualmente: Item anItem = // qui il codice di caricamento di un item // cancellazione manuale di tutte le insanze bid legate all’oggetto item for ( Iterator<Bid> it = anItem.getBids().iterator(); it.hasNext(); ) { Bid bid = it.next(); it.remove(); // Rimozione dei bid dalla collection session.delete(bid); // Eliminazione dei bid dal DB } session.delete(anItem); // Infine, rimozione dell’ item dal DB 18 E’ possibile, cancellazione. però, rendere <set name="bids" inverse="true" cascade="save-update, delete"> transitiva anche l’operazione di NOTA: la cancellazione a cascata ha senso nelle composizioni (rombo pieno nel diagramma UML. [email protected] Associazione molti-a-uno transitive persistence: cascade=“save-update, delete” Gli oggetti entity possono essere condivisi tra più elementi nel modello ad oggetti. Cosa succedere se gli oggetti Bid sono associati anche con, ad esempio, un oggetto User? Item 1..* Bid 1..* User Chiave esterna verso ITEM Chiave esterna verso USER 19 [email protected] Associazione molti-a-uno transitive persistence: cascade=“save-update, delete” 20 Tentando di eliminare tutti i bid di un item con l’opzione cascade attiva, non si produrrebbero exception in quanto sul DB non si avrebbe nessuna violazione di vincoli. Però, a livello Java, un User avrebbe una collection di references a oggetti Bid che non esistono più sul DB E’ necessario rimuovere tutti i references agli oggetti bid prima di cancellare un item e, quindi, a cascata tutti i bids. E’ quindi necessario rimuovere tutti gli oggetti bid dalla collection in User. Bisogna quindi “seguire” i puntatori e gestire correttamente i legami tra gli oggetti. NOTA: una cancellazione a cascata che viola un vincolo sul DB genera una exception [email protected] Associazione molti-a-uno transitive persistence: cascade="save-update, delete, delete-orphan” Esiste una ulteriore opzione per la cancellazione di oggetti entity. 21 L’opzione delete-orphan fa si che Hibernate cancelli oggetti entity dalla collection soltanto se non vi sono ulteriori puntatori associati ad esso. Nel nostro esempio, cancellando un elemento dalla collection degli oggetti bid, questo si cancellerà soltanto se non vi sono altri oggetti che hanno un reference verso di esso. <set name="bids" inverse="true" cascade="save-update, delete, delete-orphan"> [email protected] Associazione uno-a-uno 22 Esaminiamo adesso una associazione uno-a-uno tra due classi entity La classe Address, in questo caso è un entity in quanto un oggetto address può essere condiviso tra un oggetto User e uno Shipment [email protected] Associazione uno-a-uno La classe Address avrà la seguente configurazione: <class name="Address" table="ADDRESS"> <id name="id" column="ADDRESS_ID"> <generator class="foreign"> <param name="property">user</param> </generator> </id> <property name="street" column="STREET"/> <property name="city" column="CITY"/> <property name="zipcode" column="ZIPCODE"/> </class> 23 La classe Address ora ha un identificatore. L’identificatore è di tipo speciale dedicato alle associazioni uno-a-uno. Ora, Address è subordinato a User: un Address esiste solo se esiste una User associato. Una relazione uno-a-uno significa che l’identificatore di Address fa da chiave esterna con l’identificatore di User La classe User avrà un normale identificatore [email protected] Associazione uno-a-uno La classe User e la sua configurazione: public class User { ... private Address shippingAddress; // Getters and setters } 24 <one-to-one name="shippingAddress" class="Address" cascade="save-update"/> La classe Address e la sua configurazione: public class Address { ... private User user; // Getters and setters } persistenza automatica di Address quando si fa una operazione di persistenza su User Vincolo di link tra le due chiavi primarie <one-to-one name="user" class="User" constrained="true"/> Questo tipo di mapping non determina soltanto una associazione bidirezionale uno-a-uno ma grazie all’attributo constrained determina la migliore associazione con la classe User. [email protected] Associazione uno-a-uno 25 Le tabelle sono: Quando si rende persistente un nuovo oggetto Address, questo è sempre associato ad un oggetto User. Hibernate prende la chiave primaria dell’oggetto User associato, e la usa come valore per la chiave primaria del nuovo oggetto Address quando lo inserisce nella tabella. [email protected] Associazione uno-a-uno User newUser = new User(); Address shippingAddress = new Address(); newUser.setShippingAddress(shippingAddress); session.save(newUser); NO User newUser = new User(); Address shippingAddress = new Address(); shippingAddress.setUser(newUser); newUser.setShippingAddress(shippingAddress); session.save(newUser); SI 26 A questo punto possiamo procedere con la creazione di un User con il suo Address. Conviene quindi modificare uno dei metodi set: NOTA: questo produce un errore! Manca il legame tra l’oggetto shippingAddress e l’user! Questo è un problema logico di OO non derivato da Hibernate! Oppure si modifica il metodo setUser nella classe Address public void setAddress(Address address) { address.setUser(this); this.address = address; } [email protected] Associazione uno-a-uno 27 E’ possibile realizzare diversamente una associazione uno-a-uno tra tabelle. Si può aggiungere una chiave esterna in una tabella verso la chiave primaria dell’altra. Il modello relazionale indica come questa soluzione non sia altro che una relazione uno-a-molti con la colonna chiave esterna vincolata a non avere duplicati (vincolo unique). Hibernate segue esattamente queste indicazioni. Infatti, nonostante si tratti di una associazione uno-a-uno, Hibernate vuole un tag <many-to-one> se si intende seguire questa strategia. [email protected] Associazione uno-a-uno Modifichiamo la configurazione della classe User: <class name="User" table="USERS"> …. <many-to-one name="shippingAddress" class="Address" column="SHIPPING_ADDRESS_ID" cascade="save-update" unique="true"/> </class> A questo punto va aggiunto un riferimento anche in Address: <one-to-one name="user“ class="User" property-ref="shippingAddress"/> 28 NOTA: adottando questa soluzione, è possibile far condividere un oggetto address con diversi tipi di oggetti: basta aggiungere più chiavi esterne. <property-ref> indica ad Hibernate che la proprietà user della classe Address rappresenta l’altro lato dell’associazione. [email protected] Associazione uno-a-uno 29 Le tabelle sono queste: [email protected] Associazione uno-a-uno opzionale 30 Consideriamo l’associazione tra Item e Shipment. Questa è una associazione uno-a-uno opzionale, cioè un item potrebbe essere in associazione con un Shipment ma non è obbligatorio. Il modello relazionale ci suggerisce una soluzione basata su una tabella intermedia. [email protected] Associazione uno-a-uno opzionale 31 Le tabelle sono queste: Quando un Item è in associazione con Shipment, si produce una nuova riga nella tabella intermedia. Il vantaggio di questa soluzione è che l’associazione è rappresentata ad di fuori delle tabelle ITEM e SHIPMENT e che, inoltre, non è necessario aggiungere chiavi esterne con valori nulli. [email protected] Associazione uno-a-uno opzionale Hibernate possiede una soluzione elegante per rappresentare in maniera trasparente la tabella intermedia. Essendoci delle chiavi esterne, queste andrebbero configurate con il tag <many-to-one> ma nella tabella intermedia. Hibernate possiede un tag apposito per mappare le tabelle intermedie: <join>. <class name="Shipment" table="SHIPMENT"> Nome tabella intermedia <id name="id" column="SHIPMENT_ID">...</id> ... 32 <join table="ITEM_SHIPMENT" optional="true"> <key column="SHIPMENT_ID"/> <many-to-one name="auction" column="ITEM_ID" not-null="true" unique="true"/> </join> </class> chiave primaria della tabella ITEM_SHIPMENT (che fa anche da chiave esterna verso SHIPMENT Chiave esterna verso ITEM rende la relazione uno-a-uno [email protected] Associazione uno-a-molti 33 Per le associazioni uno-a-molti, la migliore collection che si può scegliere è bag. Hibernate, non dovendo mantenere l’ordine di inserimento o controllare se vi sono valori duplicati, aggiunge elementi alla collezioni senza doverla caricare in memoria. Questo è fondamentale se si intende manipolare grandi insiemi di dati. [email protected] Associazione uno-a-molti Se in una collection si deve mantenere la posizione degli elementi, la scelta migliore è List ma tali posizioni si possono aggiungere in una colonna che fa da indice. <class name="Item“ table="ITEM"> ... <list name="bids"> <key column="ITEM_ID“not-null="true"/> <list-index column="BID_POSITION"/> <one-to-many class="Bid"/> </list> </class> Qui riportiamo l’esempio di associazione uno-a-molti unidirezionale tra Item e Bid 34 Deve essere not-null se l’associazione è unidirezionale [email protected] Associazione uno-a-molti 35 Se, però, consideriamo l’associazione bidirezionale tra Item e Bid, L’aggiunta di una colonna indice provoca problemi per la gestione unilaterale della persistenza (dovuta all’attributo inverse=“true”). L’attributo inverse si può aggiungere solo nella classe che contiene la collezione e, quando è true, significa che Hibernate ignora le operazioni fatte su di essa. Nel nostro esempio, Hibernate ignora le operazioni fatte sull’oggetto item alla sua (interna) lista dei bids; solo lo stato degli oggetti Bid è condierato da Hibernate per la sincronizzazione sul DB. Ma come fa Hibernate a gestire l’indice se ignora la collezione dei Bids? E’ necessario invertire la logica dell’inverse!! [email protected] Associazione uno-a-molti Non è possibile aggiungere l’attributo inverse nel mapping della classe Bid ma si può ottenere lo stesso effetto aggiungendo gli attributi insert e update posti a false. <class name="Bid“ table="BID"> ... <many-to-one name="item“ column="ITEM_ID" class="Item" not-null="true" insert="false" update="false"/> </class> <class name="Item" table="ITEM"> ... <list name="bids" inverse="false"> <key column="ITEM_ID“ not-null="true"/> <list-index column="BID_POSITION"/> <one-to-many class="Bid"/> </list> </class> Insert e update messi a false significa che gli oggetti Bid sono read-only 36 Di default è false, quindi si può omettere NOTA: tale inversione è necessaria per tutte le collezioni che mantengono la posizione degli elementi (quindi indicizzabili): List, Map e array. [email protected] Associazione molti-a-molti Nei progetti reali non vi sono molte associazioni molti-a-molti. Normalmente, è possibile rappresentare tale associazione con due associazioni molti-a-uno. Consideriamo il seguente esempio: Category 37 1..* presenta > 1..* Item Il modello relazionale prevede una tabella intermedia per questo tipo di associazione. Hibernate segue questa logica, prevedendo un mapping con una tabella intermedia. [email protected] Associazione molti-a-molti 38 Le tabelle sono: La classe intermedia ha una chiave esterna per ognuna delle due tabelle in relazione; queste, insieme fanno chiave primaria. Eventualmente possono essere presenti altre colonne [email protected] Associazione molti-a-molti unidirezionale Per realizzare una associazione unidirezionale, basta aggiungere una collezione in una delle due classi. Ad esempio, aggiungiamo nella classe Category una collezione di oggetti item : <set name="items“ table="CATEGORY_ITEM" cascade="save-update"> <key column="CATEGORY_ID"/> <many-to-many class="Item“ column="ITEM_ID"/> </set> Chiave esterna nella tabella intermedia 39 public class Category { private Set<Item> items = new HashSet<Item>(); … public void addItem(Item item) { this.items.add(item); } //metodi get/set Il tag <many-to-many> indica la presenza di una tabella intermedia verso Item [email protected] Associazione molti-a-molti unidirezionale E’ possibile utilizzare una collection Bag se si prevede la presenza di duplicati o una lista eventualmente indicizzata: <idbag name="items“ table="CATEGORY_ITEM” cascade="save-update"> <collection-id type="long“column="CATEGORY_ITEM_ID"> <generator class=“increment"/> <key column="CATEGORY_ID"/> </collection-id> <many-to-many class="Item" column="ITEM_ID"/> </idbag> 40 <list name="items“ table="CATEGORY_ITEM" cascade="save-update"> <key column="CATEGORY_ID"/> <list-index column="DISPLAY_POSITION"/> <many-to-many class="Item" column="ITEM_ID"/> </list> [email protected] Associazione molti-a-molti bidirezionale Proviamo a realizzare la stessa associazione ma bidirezionale <class name="Category" table="CATEGORY"> ... <set name="items“ table="CATEGORY_ITEM" cascade="save-update"> <key column="CATEGORY_ID"/> <many-to-many class="Item" column="ITEM_ID"/> </set> NOTA: l’attributo inverse può essere posizionato a scelta in una delle due collection. 41 L’attributo cascade è inutile per le associazioni molti-amolti <class name="Item" table="ITEM"> ... <set name="categories“ table="CATEGORY_ITEM” inverse="true" cascade="save-update"> <key column="ITEM_ID"/> <many-to-many class="Category“ column="CATEGORY_ID"/> </set> </class> [email protected] Associazione molti-a-molti bidirezionale E’ possibile scegliere un tipo di collection diverso per le due classi. NOTA: Non è possibile utilizzare collections indicizzate dal lato inverse della associazione, il che significa anche che non possono essere entrambe indicizzate 42 [email protected] Associazione molti-a-molti aggiungere colonne alla tabella intermedia Il modello relazionale prevede l’ aggiunta di colonne alla tabella intermedia della associazione molti-a-molti per rappresentare informazioni inerenti al legame che intercorre tra due record. Hibernate consente a sua volta l’aggiunta di colonne nel mapping della associazione. Aggiungiamo ad esempio, due colonne alla tabella intermedia: nome dell’user e data dell’operazione 43 [email protected] Associazione molti-a-molti aggiungere colonne alla tabella intermedia Esistono due soluzioni diverse al problema. La prima e più semplice prevede la creazione di una nuova classe di tipo value (nel nostro esempio potremmo chiamarla CategorizedItem): public class CategorizedItem { private String username; private Date dateAdded = new Date(); private Item item; private Category category; 44 public CategorizedItem(String name, Category category, Item item) { this.category = category; this.item = item; } ... // metodi get/set } Essendo la classe di tipo value, non c’è un campo identificatore. Sempre in quanto classe value, deve essere subordinato ad una classe entity: scegliamo Category [email protected] Associazione molti-a-molti aggiungere colonne alla tabella intermedia 45 La classe Category va quindi mappata con una collezione di oggetti value di tipo CategorizedItem. <class name="Category" table="CATEGORY"> La chiave primaria è ... composta da tutte le colonne <set name="categorizedItems" table="CATEGORY_ITEM"> della tabella intermedia <key column="CATEGORY_ID"/> <composite-element class="CategorizedItem"> Tutte le colonne devono <parent name="category"/> essere not-null (vedi mapping <many-to-one name="item" column="ITEM_ID" delle collection di oggetti not-null="true" value) class="Item"/> <property name="username" column="ADDED_BY_USER"/> <property name="dateAdded" column="ADDED_ON"/> </composite-element> </set> Le due colonne aggiuntive </class> [email protected] Associazione molti-a-molti aggiungere colonne alla tabella intermedia: associazione ternaria Approfittiamo dell’esempio per mostrare il mapping di una associazione ternaria: <set name="categorizedItems“ table="CATEGORY_ITEM"> <key column="CATEGORY_ID"/> <composite-element class="CategorizedItem"> <parent name="category"/> <many-to-one name="item" column="ITEM_ID" not-null="true" class="Item"/> <many-to-one name="user" column="USER_ID" not-null="true" class="User"/> <property name="dateAdded" column="ADDED_ON"/> </composite-element> </set> 46 L’user lo rappresentiamo come associazione con la classe User e non solo con il nome [email protected] Associazione molti-a-molti aggiungere colonne alla tabella intermedia Per aggiungere elementi in associazione tra Item e Category, agiremo attraverso gli oggetti category. Qui consideriamo la colonna aggiuntiva userName CategorizedItem unLink = new CategorizedItem(unUser.getUserName(), unaCategory, unItem); aCategory.getCategorizedItems().add( aLink ); aCategory.getCategorizedItems().remove( aLink ); 47 La classe CategorizedItem è subordinata alla classe Category, per cui la navigazione verso gli oggetti item non è possibile (gli oggetti value non sono raggiungibili da altri tipi di oggetti) [email protected] Associazione molti-a-molti unidirezionale utilizzando collection map Proviamo ad utilizzare collection di tipo Map per le associazioni molti-amolti. Partiamo dal presupposto che le chiavi delle mappe sono oggetti di tipo value mentre i valori sono di tipo entity Consideriamo il seguente esempio dove la classe Item possiede una collection Map le cui chiavi sono gli identificatori dei bid e i valori, i references verso gli oggetti bid. Item 48 Map 1..* (1, puntatore all’oggetto bid 1) (2, puntatore all’oggetto bid 2) …. 1..* Bid NOTA: la composizione delle tabelle sul DB non cambia rispetto agli esempi precedenti [email protected] Associazione molti-a-molti unidirezionale utilizzando collection map La configurazione della mappa per la classe Item è: <map name="bidsByIdentifier"> <key column="ITEM_ID"/> <map-key type="long" formula="BID_ID"/> <one-to-many class="Bid"/> </map> Cosa succede se la chiave della mappa è un oggetto entity? Si ottiene il mapping di una associazione ternaria. Item 49 Map La presenza della formula rende la colonna ITEM_ID read-only 1..* 1..* Bid (puntatore ad un oggetto User x, puntatore all’oggetto bid a) (puntatore ad un oggetto User y, puntatore all’oggetto bid b) …. Nel nostro esempio, potremmo avere come chiave della mappa un oggetto User (si intende un puntatore ad un oggetto User). [email protected] Associazione molti-a-molti bidirezionale utilizzando collection map 50 Le tabelle sono queste: [email protected] Associazione molti-a-molti unidirezionale utilizzando collection map Nel mapping si introduce un nuovo tag appositamente pensato per questi casi: <map name="itemsAndUser“ table="CATEGORY_ITEM"> <key column="CATEGORY_ID"/> <map-key-many-to-many column="ITEM_ID" class="Item"/> <many-to-many column="ADDED_BY_USER_ID" class="User"/> </map> Il tag <map-key-many-to-many> crea un link tra le tre classi. Per aggiungere un elemento alla mappa: aCategory.getItemsAndUser().add( anItem, aUser ); 51 Infine, si potrebbe rendere l’associazione bidirezionale aggiungendo una collection di oggetti category nella classe Item [email protected] Domande? join constrained one-to-one inverse One-to-many cascade not-null bidirezionale 52 many-to-many
© Copyright 2024 Paperzz