10 Hibernate3 Mapping delle collections

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