Hibernate Mapping delle collections Dott. Doria Mauro [email protected] [email protected] Mapping delle collections di elementi di tipo value primitivi o Stringhe Una classe di tipo value: – – 2 Non ha una propria identità ne un proprio ciclo di vita. E’ legato ad un solo aggetto entity e non può essere condiviso tra più oggetti entity Di solito ad una classe value non corrisponde una tabella sul DB, ma questo non è sempre vero. Infatti, se ad una classe entity si associa una collezione di oggetti value, è necessario creare una tabella, detta collection-table. Java offre molti classi di tipo Collection in base alle proprie esigenze di progetto. [email protected] Mapping delle collections di elementi di tipo value primitivi o Stringhe Supponiamo di avere la seguente situazione: Item 3 0..* String fileName:String Si vogliono associare una serie di immagini ad un oggetto Item. In questo primo esempio si è interessati soltanto al nome dell’immagine. La classe String, naturalmente, è di tipo value. La classe Item, quindi, ha come proprietà una collezione di tipo String: Public class Item { private <<Interface>> images = new <<Implementation>>(); ... // Getter and setter methods [email protected] Mapping delle collections 4 E’ possibile anche utilizzare i generics ma è facoltativo. E’ possibile anche descrivere la natura degli oggetti collezionati in fase di configurazione. Quale tipo di Collection utilizzare? Hibernate supporta Collection, Map ed array. Hibernate semplicemente wrappa l’oggetto collection [email protected] Mapping delle collections 5 Le interfacce supportate sono: – java.util.Set. Il tag nella configurazione è <set>. Non ammette duplicati (la classe reale deve essere un java.util.HashSet). – java.util.SortedSet. Il tag è <set> con l’attributo sort che a cui si può indicare o un Comparator oppure un ordinamento naturale (la classe reale deve essere un java.util.TreeSet). – java.util.List. Il tag è <list>. Mantiene l’ordine di inserimento (la classe reale deve essere un java.util.ArrayList). – java.util.Collection. Per rappresentare il tipo “bag” (lista con duplicati senza ordinamento). Il tag è <bag> o <idbag>. Hibernate supporta il tipo Bag anche se Java non ne ha uno suo. Utilizza un oggetto List e ignore l’indice. (la classe reale deve essere un java.util.ArrayList). [email protected] Mapping delle collections 6 – java.util.Map. Il tag è <map>. Insieme di coppie [chiave,valore]. (la classe reale deve essere un (la classe reale deve essere un java.util.HashMap). – java.util.SortedMap. Il tag è <map> con l’attributo sort che a cui si può indicare o un Comparator oppure un ordinamento naturale (la classe reale deve essere un java.util.TreeMap). – array sono supportati. Il tag è <primitive-array> per gli arrays di primitivi java e <array> per arrays di oggetti. Si utilizzano raramente. E’ possibile mappare anche collezioni non direttamente supportate da Hibernate. E’ una operazione molto complessa che prevede l’estensione delle classi PersistentSet, PersistentBag, or PersistentList a seconda delle necessità. [email protected] Mapping delle collection Set Tornando all’esempio descritto in precedenza, inizialmente le immagini composte soltanto dal nome. La classe Item ha una collection di tipo String: public class Item { private Set images = new HashSet(); ... public Set getImages() { return this.images; } public void setImages(Set images) { this.images = images; } 7 La corrispondente configurazione : consideriamo public void addImages(String image) { this.images.add(image); } Chiave esterna verso la tabella ITEM <set name="images" table="ITEM_IMAGE"> <key column="ITEM_ID"/> <element type="string" column="FILENAME" not-null="true"/> </set> Dichiarazione degli elementi della collection (di tipo value) [email protected] Mapping delle collection Set 8 Hibernate costruirà una tabella dedicata alla collection. Tale tabella è trasparente all’applicazione ed è gestita da Hibernate. La chiave primaria della tabella ITEM_IMAGE si compone di tutte le colonne in modo che non vi siano duplicati (caratteristica delle collection di tipo Set) [email protected] Mapping delle collection Bag 9 Se vogliamo associare più volte la stessa immagine per lo stesso oggetto Item, la collection Set è inappropriata. Proviamo con Bag Il tipo Bag non è previsto in Java ma l’interfaccia Collection ha la semantica adeguata, quindi è possibile simularla. Esistono 2 modi per mappare un oggetto Bag: – Dichiarare la proprietà di tipo java.util.Collection ed utilizzare come classe concreta java.util.ArrayList. Configurare con il tag <bag> o <idbag> – Dichiarare la proprietà di tipo java.util.List ed utilizzare come classe concreta java.util.ArrayList. Configurare con il tag <bag> o <idbag> NOTA: Il secondo modo è sconsigliato in quanto dichiarando la proprietà di tipo List, si da l’impressione di poter accedere agli elementi tramite un indice. [email protected] Mapping delle collection Bag La configurazione è: <idbag name="images" table="ITEM_IMAGE"> <collection-id type="long" column="ITEM_IMAGE_ID"> <generator class="sequence"/> </collection-id> <key column="ITEM_ID"/> <element type="string" column="FILENAME" not-null="true"/> </idbag> NOTA: native non è supportato dal tag <idbag> 10 Chiave primaria della tabella (autoincremento) Chiave esterna verso la tabella ITEM Dichiarazione degli elementi della collection (di tipo value) [email protected] Mapping delle collection List La configurazione è: <list name="images" table="ITEM_IMAGE"> <key column="ITEM_ID"/> <list-index column="POSITION"/> <element type="string" column="FILENAME" not-null="true"/> </list> NOTA: l’indice della lista parte da zero. Per cambiare usare <list-index base="1".../> 11 Chiave esterna verso la tabella ITEM Indice di posizionamento nella lista Dichiarazione degli elementi della collection (di tipo value) NOTA: Per i valori di POSITION mancanti, gli elementi nella lista Java saranno a null. [email protected] Mapping delle collection Map La configurazione è: <map name="images" table="ITEM_IMAGE"> <key column="ITEM_ID"/> <map-key column="IMAGENAME" type="string"/> <element type="string" column="FILENAME" not-null="true"/> </map> Chiave esterna verso la tabella ITEM Colonna per le chiavi della mappa Colonna per i valori della mappa 12 Le colonne ITEM_ID e IMAGENAME fanno da primary key della tabella [email protected] Mapping delle collection Sorted La configurazione è: <map name="images“ table="ITEM_IMAGE“ sort="natural"> <key column="ITEM_ID"/> <map-key column="IMAGENAME" type="string"/> <element type="string" column="FILENAME" not-null="true"/> </map> Tipo di ordinamento <set name="images“ table="ITEM_IMAGE“ sort="natural"> <key column="ITEM_ID"/> <element type="string" column="FILENAME" not-null="true"/> </set> L’attributo sort può assumere: – 13 – natural : ordinamento basato sull’invocazione del metodo compareTo() della classe String (per le mappe) o della classe si cui si compone il set (per i Set). myPackage.MyComparator [email protected] Mapping delle collection Sorted In alternativa, è possibile far ordinare le righe al DB : <map name="images“ table="ITEM_IMAGE" order-by="IMAGENAME asc"> Clausola order by di SQL <key column="ITEM_ID"/> <map-key column="IMAGENAME" type="string"/> <element type="string" column="FILENAME" not-null="true"/> </map> <set name="images“ table="ITEM_IMAGE“ order-by="FILENAME asc"> <key column="ITEM_ID"/> <element type="string" column="FILENAME" not-null="true"/> </set> L’attributo order-by può assumere: – 14 – nomeColonna [asc o desc]: ordinamento come da clausola order by di SQL. Hibernate utilizza il valore di questa clausola direttamente nelle sue query. funzione SQL: ad esempio order-by = lower(FILENAME) asc [email protected] Mapping delle collection Sorted NOTA: Utilizzando la clausola order-by, le collections Java possono essere dichiarate non sorted: per i map si può utilizzare il java.util.Map e non java.util.SortedMap ( analogamente per i Set) NOTA: Nel caso dei Map (e analogamente per i Set), mentre la collection è costruita regolarmente: java.util.Map myMap = new java.util.HashMap(); 15 Hibernate wrappa l’oggetto Map con un oggetto di tipo LinkedHashMap (analogamente LinkedHashSet); questo preserva l’ordine con cui sono stati inseriti gli elementi nella collezione; quando la collection viene caricata, Hibernate è in grado di restituire gli elementi nello stesso ordine con cui sono stati inseriti. [email protected] Mapping delle collection Sorted NOTA: E’ possibile utilizzare la clausola order-by anche per il tipo Bag. Per questo tipo, inoltre, Hibernate è in grado di preservare l’ordine di inserimento senza utilizzare il warepping (essendoci alla base un ArrayList) NOTA: L’ordine di inserimento per i Map e i Set non è preservato se si utilizza una versione di Java precedente alla 1.4. I tipi Linked* sono stati inseriti in Java a partire da tale versione. 16 [email protected] Mapping delle collections di elementi di classi value (componenti) Supponiamo di avere la seguente situazione: Item 0..* Image name: String fileName:String sizeX:int sizeY:int 17 In questo esempio, si è interessati ad altri dati oltre al nome dell’immagine, per cui aggiungiamo la classe Image allo schema. La classe Image è subordinata alla classe Item, quindi, è di tipo value. La classe Item, ha come proprietà una collezione di tipo Image: Public class Item { private <<Interface>> images = new <<Implementation>>(); ... // Getter and setter methods [email protected] Mapping delle collections di elementi di classi value (componenti) La classe Image, va definita come un POJO senza campo identificativo. Vanno aggiunti i metodi equals() e compareTo() per essere utilizzati nelle collections. – 18 Non tutte le collections richiedono entrambi i metodi ma meglio aggiungerli per poter poi essere liberi di scegliere la collections che si desidera. NOTA: E’ possibile utilizzare i generics ma Hibernate non lo richiede. NOTA: se la classe Image non fosse stata di tipo value (ma di tipo entity) avremmo dovuto mappare una associasione uno-amolti tra Item e Image. [email protected] Mapping delle collection Set di elementi di classi value (componenti) La configurazione è: <set name="images“ table="ITEM_IMAGE“ order-by="IMAGENAME asc"> <key column="ITEM_ID"/> <composite-element class="Image"> <property name="name" column="IMAGENAME" not-null="true"/> <property name="filename" column="FILENAME" not-null="true"/> <property name="sizeX" column="SIZEX" not-null="true"/> <property name="sizeY" column="SIZEY" not-null="true"/> </composite-element> </set> 19 La differenza rispetto al precedente caso è la presenza del tag <composite-element> al posto di <element> Tutti i campi dei Image devono essere not-null in quanto faranno tutti parte della chiave primaria. [email protected] Mapping delle collection Set di elementi di classi value (componenti) Se si considera che Image non ha un suo identificatore e che la collection non ammette duplicati, Hibernate deve trattare una intera riga di ITEM_IMAGE come univoca. La presenza del campo ITEM_ID nella chiave, garantisce la possibilità di che due Item abbiano la stessa immagine (senza però sharing dell’oggetto Image). UN OGGETTO IMAGE 20 La chiave primaria della tabella ITEM_IMAGE è composta da tutte le colonne (per questo devono essere not-null) [email protected] Mapping delle collection Set di elementi di classi value (componenti) L’associazione è al momento unidirezionale. Per accedere alla collezione, il modo più generale è: anItem.getImages().iterator() E’ possibile modificare l’associazione rendendola bidirezionale in modo da poter scrivere: anItem = anImage.getItem() 21 <set name="images“ table="ITEM_IMAGE“ order-by="IMAGE_NAME asc"> <key column="ITEM_ID"/> <composite-element class="Image"> <parent name="item"/> <property name="name" column="IMAGENAME" not-null="true"/> <property name="filename" column="FILENAME" not-null="true"/> <property name="sizeX" column="SIZEX" not-null="true"/> <property name="sizeY" column="SIZEY" not-null="true"/> </composite-element> Riferimento </set> all’oggetto padre Nota: una vera bidirezionalità è impossibile. Si può scrivere una query per recuperare una Image ma si ottengono valori scalari e il riferimento al padre è null [email protected] Mapping delle collection Bag di elementi di classi value (componenti) Dover vincolare tutte le colonne ad essere not-null può essere un problema. Per avere una collection Set senza le colonne not-null bisogna utilizzare una collection Bag e gestire i duplicati via codice. <idbag name="images“ table="ITEM_IMAGE“ order-by="IMAGE_NAME asc"> <collection-id type="long" column="ITEM_IMAGE_ID"> <generator class="sequence"/> </collection-id> Colonna chiave <key column="ITEM_ID"/> primaria <composite-element class="Image"> <property name="name" column="IMAGENAME"/> <property name="filename" column="FILENAME" not-null="true"/> <property name="sizeX" column="SIZEX"/> <property name="sizeY" column="SIZEY"/> </composite-element> </idbag> 22 Ricordarsi di dichiarare nella classe Item la collection per i Bag: private Collection images = new ArrayList(); [email protected] Mapping delle collection Bag di elementi di classi value (componenti) Hibernate aggiunge una colonna per creare una chiave primaria indipendente dalle altre colonne: NOTA: In questo caso, Image sembrerebbe avere una propria identità. In effetti, la struttura delle tabelle è uguale a quella delle relazioni uno-a-molti tra entitys. In questo ultimo caso, però, è necessario gestire (via codice) la condivisione di oggetti Image tra più entity. 23 [email protected] Mapping delle collection Map di elementi di classi value (componenti) Per gli oggetti Map la configurazione è: <map name="images“ table="ITEM_IMAGE“ order-by="IMAGENAME asc"> <key column="ITEM_ID"/> <map-key type="string" column="IMAGENAME"/> <composite-element class="Image"> <property name="filename" column="FILENAME" not-null="true"/> <property name="sizeX" column="SIZEX"/> <property name="sizeY" column="SIZEY"/> </composite-element> </map> 24 La chiave primaria è composta dalle colonne ITEM_ID e IMAGENAME Nel nostro esempio, è opportuno rimuovere il campo name della classe Image (perchè diventa la key della mappa) NOTA: il tag <composite-element> può contenere altri componenti (<nestedcomposite- element>) o relazioni molti-a-uno (<many-to-one>) ma non può avere una sottocollection. [email protected] Mapping delle collection List di elementi di classi value (componenti) 25 Sul libro non c’è!!! [email protected] Domande? List Logging SortedBag component Mapping Bag Value class Collection List 26 SortedMap