Questa sezione non contiene molto codice Java di esempio. Diamo per scontato che conosciate già come usare l'infrastruttura delle collezioni in Java. Se è così, non c'è davvero niente di più da sapere - con un'unica avvertenza, potete usare le collezioni di Java nello stesso modo in cui avete sempre fatto.
Hibernate può rendere persistenti istanze di java.util.Map, java.util.Set, java.util.SortedMap, java.util.SortedSet, java.util.List, e qualsiasi array di entità persistenti o valori. Le proprietà di tipo java.util.Collection or java.util.List possono anche essere rese persistenti con semantiche a "sacco" ("bag").
Ed ora l'avvertenza: le collezioni persistenti non mantengono nessuna semantica aggiunta dalla classe che implementa l'interfaccia di base della collezione particolare (come ad esempio l'ordine di iterazione in un LinkedHashSet). Le collezioni persistenti in particolare si comportano come HashMap, HashSet, TreeMap, TreeSet e ArrayList rispettivamente. Inoltre, il tipo di oggetto java di una proprietà che contiene una collezione, deve essere quello dell'interfaccia (ovvero Map, Set o List; mai HashMap, TreeSet o ArrayList). Questa restrizione esiste perché, a vostra insaputa, Hibernate sostituisce le istanze di Map, Set e List con istanze delle sue implementazioni persistenti di queste interfacce. Per questo motivo, dovete anche fare attenzione quando usate == sulle collezioni.
Cat cat = new DomesticCat(); Cat kitten = new DomesticCat(); .... Set kittens = new HashSet(); kittens.add(kitten); cat.setKittens(kittens); session.save(cat); kittens = cat.getKittens(); //Ok, la collezione "kittens" è un Set (HashSet) cat.getKittens(); //Errore!
Le collezioni rispettano le normali regole per i tipi di valore: niente riferimenti condivisi, vengono create e cancellate insieme all'entità che le contiene. A causa delle caratteristiche del modello relazionale sottostante, non sopportano semantiche a valore nullo: Hibernate non distingue tra il riferimento ad una collezione nulla e una collezione vuota.
Le collezioni sono automaticamente rese persistenti quando sono referenziate da un oggetto persistente, e cancellate automaticamente quando il riferimento viene eliminato. Se una collezione viene passata da un oggetto persistente ad un altro, i suoi elementi vengono spostati da una tabella ad un'altra. Non dovreste preoccuparvi molto di questo: semplicemente usate le collezioni di Hibernate nello stesso modo in cui usate le normali collezioni di Java, ma assicuratevi di comprendere la semantica delle associazioni bidirezionali (discussa più avanti), prima di farlo.
Le istanze di collezione sono distinte nel database da una chiave esterna verso l'entità che le contiene. Questa chiave esterna viene chiamata la chiave di collezione. La chiave di collezione è mappata con l'elemento <key>.
Le collezioni possono contenere quasi ogni altro tipo di Hibernate, compresi tutti i tipi di base, i tipi personalizzati (custom), i tipi di entità e i componenti. Questa è una definizione importante: un oggetto in una collezione può essere gestito sia con una semantica di "passaggio per valore" (del resto dipende completamente dal proprietario della collezione) o può essere un riferimento ad un'altra entità di Hibernate, con il suo proprio ciclo di vita. Le collezioni non possono contenere altre collezioni. Il tipo contenuto viene chiamato il tipo di elemento della collezione. Gli elementi della collezione vengono mappati da<element>, <composite-element>, <one-to-many>, <many-to-many> or <many-to-any>. I primi due mappano elementi con semantica di valore, mentre gli altri tre vengono usati per mappare associazioni tra entità.
Tutti i tipi di collezione eccetto Set e bag hanno una colonna indice , ovvero una colonna che mappa l'indice di un array o di una List o la chiave di una Map. L'indice di una Map può essere di qualsiasi tipo di base, un tipo di entità o anche un tipo composito (ma non può essere una collezione). L'indice di un array o di una lista è sempre di tipo integer. Gli indici vengono mappati usando <index>, <index-many-to-many>, <composite-index> o <index-many-to-any>.
C'è un insieme abbastanza vario di mappaggi che possono venire generati per le collezioni, e coprono molti modelli relazionali comuni. Suggeriamo che sperimentiate con lo strumento di generazione dello schema per avere un'idea di come i vari tipi di dichiarazione si traducono in tabelle di database.
Le collezioni vengono dichiarate tramite gli elementi <set>, <list>, <map>, <bag>, <array> e <primitive-array>. <map> è un buon esempio:
<map name="propertyName" (1) table="table_name" (2) schema="schema_name" (3) lazy="true|false" (4) inverse="true|false" (5) cascade="all|none|save-update|delete|all-delete-orphan" (6) sort="unsorted|natural|comparatorClass" (7) order-by="column_name asc|desc" (8) where="arbitrary sql where condition" (9) outer-join="true|false|auto" (10) batch-size="N" (11) access="field|property|ClassName" (12) > <key .... /> <index .... /> <element .... /> </map>
(1) | name il nome della proprietà corrispondente alla collezione |
(2) | table (opzionale - se assente è uguale al nome della proprietà) il nome della tabella che corrisponde alla collezione (non usato per le associazioni uno-a-molti) |
(3) | schema (opzionale) il nome di uno schema di tabella che sovrascriva quello dichiarato sull'elemento radice |
(4) | lazy (opzionale - se assente è false) consente l'inizializzazione differita (non usato per gli array) |
(5) | inverse (opzionale - se assente vale false) indica che questa collezione è il lato "opposto" di una associazione bidirezionale |
(6) | cascade (opzionale - se assente vale none) consente che le operazioni si propaghino sugli elementi figli della collezione |
(7) | sort (opzionale) specifica una collezione ordinata con un metodo di ordinamento naturale, o una classe di comparazione specifica |
(8) | order-by (opzionale, solo JDK1.4) specifica una colonna della tabella (o più colonne) che indica l'ordine di iterazione della Map, del Set o del bag, con un indicatore asc o desc (ascendente o discendente) opzionale. |
(9) | where (opzionale) specifica una condizione WHERE opzionale da usare quando si carica o rimuove la collezione (utile se la collezione deve contenere solo un sottoinsieme dei dati presenti) |
(10) | outer-join (opzionale) specifica che la collezione dovrebbe essere caricata tramite un outer join, quando possibile. Solo una collezione può venire caricata in questo modo in una SELECT SQL. |
(11) | batch-size (opzionale, per default vale 1) specifica una dimensione del "batch" (blocco di caricamento) per il caricamento differito di istanze di questa collezione. |
(12) | access (opzionale - se assente vale property): La strategia che Hibernate dovrebbe utilizzare per accedere al valore di questa proprietà. |
Il mappaggio di una List o di un array richiede una colonna separata della tabella per mantenere l'indice (l'elemento i in foo[i]). Se il vostro modello relazionale non ha una colonna indice, ad esempio perché state lavorando con dati preesistenti, usate un Set non ordinato. Questo sembra deludere le persone che assumono che una List sia un modo più conveniente di accedere ad una collezione non ordinata, ma le collezioni di Hibernate obbediscono strettamente alla semantica associata alle interfacce Set, List e Map, e semplicemente gli elementi di una List non si ordinano spontaneamente!
Da un altro punto di vista, le persone che immaginavano di usare List per emilare la semantica di un bag hanno un motivo legittimo di lamentela, qui. Un bag è una collezione non ordinata e non indicizzata di elementi, che può quindi contenere lo stesso elemento più volte. L'infrastruttura delle collezioni di java non specifica un'interfaccia Bag, per cui la si deve emulare con una List. Hibernate consente di mappare proprietà di tipo List o Collection con l'elemento <bag>. Notate che la semantica del sacco (bag) non sono realmente parte del contratto di Collection ed in realtà confliggono con il contratto della List contract (anche se, come discusso più avanti nel capitolo, potete ordinare a piacimento il bag).
Nota: bag molto grandi mappati con inverse="false" in Hibernate sono inefficienti, e andrebbero evitati; Hiberante non può creare, cancellare o aggiornare righe individualmente perché non c'è una chiave che possa identificare una riga singola.
Una tabella di collezione è richiesta per ogni collezione di valori o di riferimenti ad altre entità che sia mappada come un'associazione molti-a-molti (la semantica naturale per una collezione java). La tabella richiede colonne di chiave (esterna), colonne di elemento e possibilmente colonne indice.
La chiave esterna dalla tabella di collezione verso la tabella della classe proprietaria è dichiarata usando un elemento <key>.
<key column="column_name"/>
(1) | column (obbligatorio): Il nome della colonna di chiave esterna. |
Per le collezioni indicizzate come le mappe e le liste, è necessario un elemento <index>. Per le liste, questa colonna deve contenere interi in sequenza, numerati a partire da zero. Assicuratevi che il vostro indice parta davvero da zero, se dovete avere a che fare con dati preesistenti. Per le mappe, la colonna può contenere valori di un tipo qualsiasi gestito da Hibernate.
<index column="column_name" (1) type="typename" (2) />
(1) | column (obbligatorio): Il nome della colonna che contiene i valori dell'indice di collezione. |
(2) | type (opzionale, se assente vale integer): Il tipo dell'indice di collezione. |
In alternativa, una mappa può essere indicizzata da oggetti di tipo "entità". Usiamo in questo caso l'elemento <index-many-to-many>.
<index-many-to-many column="column_name" (1) class="ClassName" (2) />
(1) | column (obbligatorio): Il nome della colonna che contiene la chiave esterna verso i valori di indice della collezione. |
(2) | class (obbligatorio): La classe dell'entità che è usata come indice della collezione. |
Per una collezione di valori usiamo l'etichetta <element>.
<element column="column_name" (1) type="typename" (2) />
(1) | column (obbligatorio): Il nome della colonna che contiene i valori degli elementi della collezione. |
(2) | type (obbligatorio): Il tipo degli elementi della collezione. |
Una collezione di entità con la propria tabella corrisponde alla nozione relazionale di associazione molti-a-molti. Una associazione di questo tipo è il mappaggio più naturale per una collezione java, ma solitamente non rappresenta il miglior modello relazionale.
<many-to-many column="column_name" (1) class="ClassName" (2) outer-join="true|false|auto" (3) />
(1) | column (obbligatorio): Il nome della colonna con la chiave esterna verso l'elemento. |
(2) | class (obbligatorio): Il nome della classe associata. |
(3) | outer-join (opzionale - se assente vale auto): quando il parametro hibernate.use_outer_join è impostato, consente il caricamento via join esterno per questa associazione . |
Alcuni esempio. Prima di tutto, un insieme di stringhe:
<set name="names" table="NAMES"> <key column="GROUPID"/> <element column="NAME" type="string"/> </set>
Un "sacco" (bag) contenente interi (con un ordine di iterazione determinato dall'attributo order-by):
<bag name="sizes" table="SIZES" order-by="SIZE ASC"> <key column="OWNER"/> <element column="SIZE" type="integer"/> </bag>
Un array di entità - in questo caso un'associazione molti-a-molti (notate che le entità vengono gestite nel ciclo di vita dell'entità proprietaria, grazie al settaggio cascade="all"):
<array name="foos" table="BAR_FOOS" cascade="all"> <key column="BAR_ID"/> <index column="I"/> <many-to-many column="FOO_ID" class="org.hibernate.Foo"/> </array>
Una mappa da indici stringa a date:
<map name="holidays" table="holidays" schema="dbo" order-by="hol_name asc"> <key column="id"/> <index column="hol_name" type="string"/> <element column="hol_date" type="date"/> </map>
Una lista di componenti (discussi nel prossimo capitolo):
<list name="carComponents" table="car_components"> <key column="car_id"/> <index column="posn"/> <composite-element class="org.hibernate.car.CarComponent"> <property name="price" type="float"/> <property name="type" type="org.hibernate.car.ComponentType"/> <property name="serialNumber" column="serial_no" type="string"/> </composite-element> </list>
Una associazione uno a molti collega direttamente le tabelle di due classi, senza che intervenga una apposita tabella di collezione. (ciò corrisponde al modello relazionale uno-a-molti.) In questo modello, si perde un po' della semantica delle collezioni di Java:
Non è possibile avere valori null contenuti in una mappa, un insieme o una lista.
Una istanza dell'entità contenuta non può appartenere a più di una istanza della collezione.
Una istanza dell'entità contenuta non può apparire in corrispondenza di più di un valore dell'indice di collezione.
Un'associazione da Pippo a Pluto richiede l'aggiunta di una colonna chiave, e possibilmente anche di una colonna indice alla tabella della classe di entità contenuta, Pluto. Queste colonne vengono mappate usando gli elementi <key> e <index> già descritti in precedenza.
L'etichetta <one-to-many> indica un'associazione uno a molti.
<one-to-many class="ClassName"/>
(1) | class (obbligatorio): Il nome della classe associata. |
Esempio:
<set name="bars"> <key column="foo_id"/> <one-to-many class="org.hibernate.Bar"/> </set>
Notate che l'elemento <one-to-many> non ha bisogno di dichiarare alcuna colonna. Non è neppure necessario specificare il nome della tabella.
Nota Molto Importante: se la colonna <key> di una associazione <one-to-many> viene dichiarata NOT NULL, Hibernate può causare violazioni di vincoli quando crea o aggiorna le associazioni. Per prevenire questo problema, dovete usare una associazione bidirezionale con l'estremità "many" (l'insieme o il sacco) impostati a inverse="true". Per ulteriori informazioni si legga la discussioni sulle associazioni bidirezionali più avanti in questo capitolo.
Le collezioni (a differenza degli array) possono essere inizializzate in maniera differita, ovvero possono caricare il proprio stato dal database solo quando l'applicazione ha bisogno di accedervi. L'inizializzazione avviene trasparentemente per l'utente, in modo tale che l'applicazione non ha normalmente bisogno di preoccuparssene (in effetti, l'inizializzazione trasparente e differita è la ragione principale per cui Hibernate ha bisogno di implementazioni proprie delle collezioni). Nonostante ciò, se l'applicazione tenta di fare qualcosa come nel codice seguente:
s = sessions.openSession(); User u = (User) s.find("from User u where u.name=?", userName, Hibernate.STRING).get(0); Map permissions = u.getPermissions(); s.connection().commit(); s.close(); Integer accessLevel = (Integer) permissions.get("accounts"); // Error!
Può ritrovarsi di fronte ad una brutta sorpresa. Poiché la collezione dei permessi (permissions) non era stata inizializzata quando la Session è stata committata, la collezione non sarà mai capace di caricare il suo stato. La correzione consiste nel muovere la riga che legge dalla collezione subito prima del commit. (In ogni caso ci sono altre maniere più avanzate di risolvere il problema).
In alternativa, potete usare una collezione ad inizializzazione non differita. Poiché l'inizializzazione differita può portare a bachi come nel codice precedente, la "non-lazyness" (il "non differimento" o, letteralmente, la "non pigrizia") è il comportamento predefinito. Comunque, è sottinteso che l'inizializzazione differita venga usata per quasi tutte le collezioni, in particolar modo per le collezioni di entità (per questioni di efficienza).
Le eccezioni che accadono mentre si inizializza in maniera differita le collezioni sono incapsulate in una LazyInitializationException.
Potete dichiarare una collezione differita usando l'attributo opzionale lazy:
<set name="names" table="NAMES" lazy="true"> <key column="group_id"/> <element column="NAME" type="string"/> </set>
In alcune architetture applicative, in particolare quando il codice che accede ai dati con Hibernate e il codice che lo usa sono in differenti livelli applicativi, può essere un problema assicurarsi che la Session sia aperta quando una collezione viene inizializzata. Ci sono due maniere principali, per trattare questa questione:
In una applicazione basata sul web, si può usare un "servlet filter" per chiudere la Session solo alla fine della richiesta di un utente, quando la costruzione della vista è completa. Naturalmente, questo impone dei vincoli molto importanti sulla correttezza della gestione delle eccezioni nella vostra infrastruttura applicativa. È vitalmente importante che la Session venga chiusa e la transazione conclusa prima di restituire il controllo all'utente, anche quando una eccezione avviene durante la resa della vista. Il servlet filter deve essere in grado di accedere la Session perché questo sia possibile. Raccomandiamo l'uso di una variabile ThreadLocal per mangenere la Session corrente (vedere il capitolo 1, Sezione 1.4, “Giochiamo con i gatti”, per un'implementazione di esempio).
In una applicazione con uno strato di business separato, la logica applicativa deve "preparare" tutte le collezioni che saranno necessarie per lo strato web prima di ritornare. Questo significa che lo strato di business dovrebbe caricare tutti i dati che siano richiesti per un particolare caso d'uso, e restituirli allo strato web di presentazione una volta inizializzati . Solitamente, l'applicazione chiama Hibernate.initialize() per ogni collezione che sarà necessaria nello strato web (la chiamata deve avvenire prima che la sessione venga chiusa), o carica la collezione direttamente usando una query di Hibernate che comprende una clausola FETCH.
Potete anche attaccare un oggetto caricato precedentemente ad una nuova Session con update() o lock() prima di accedere a collezioni non inizializzate (o altri tipi di mediatori). Hibernate non può farlo automaticamente, perché dovrebbe introdurre semantica transazionale ad-hoc!
Potete usare il metodo filter() dell'API della classe Session di Hibernate per ottenere la dimensione di una collezione senza doverla inizializzare:
( (Integer) s.filter( collection, "select count(*)" ).get(0) ).intValue()
filter() o createFilter() vengono anche usati per caricare efficientemente sottoinsiemi di una collezione senza bisogno di inizializzare l'intera collezione.
Hibernate supporta collezioni che implementano java.util.SortedMap e java.util.SortedSet. Dovete specificare esplicitamente un comparatore nel file di mappaggio:
<set name="aliases" table="person_aliases" sort="natural"> <key column="person"/> <element column="name" type="string"/> </set> <map name="holidays" sort="my.custom.HolidayComparator" lazy="true"> <key column="year_id"/> <index column="hol_name" type="string"/> <element column="hol_date type="date"/> </map>
I valori permessi per l'attributo sort sono unsorted, natural e il nome di una classe che implementi java.util.Comparator.
Le collezioni ordinate si comportano in effetti come java.util.TreeSet o java.util.TreeMap.
Se volete che sia il database stesso ad ordinare gli elementi della collezione, usate l'attributo order-by dei mappaggi dei set, bag o delle map. Questa soluzione funziona solo sotto JDK 1.4 o superiori (è implementata usando LinkedHashSet o LinkedHashMap). Questo fa sì che l'ordinamento avvenga durante l'esecuzione della query SQL, non in memoria.
<set name="aliases" table="person_aliases" order-by="name asc"> <key column="person"/> <element column="name" type="string"/> </set> <map name="holidays" order-by="hol_date, hol_name" lazy="true"> <key column="year_id"/> <index column="hol_name" type="string"/> <element column="hol_date type="date"/> </map>
Notate che il valore dell'attributo order-by si riferisce all'SQL, non all'HQL!
Le associazioni possono anche venire ordinate usando criteri arbitrari in fase di esecuzione usando un filter().
sortedUsers = s.filter( group.getUsers(), "order by this.name" );
Se avete abbracciato il nostro punto di vista, secondo cui le chiavi composte sono una cattiva cosa, e le entità dovrebbero avere identificatori sintetici (chiavi surrogate), potreste trovare un po' strano che le associazioni molti-a-molti e le collezioni di valori che abbiamo mostrato fin qui, si mappano tutte su tabelle con chiavi composte! Ora, questo punto è abbastanza discutibile; una tabella di pura associazione non sembra trarre molto beneficio da una chiave surrogata (benché una collezione di valori composti potrebbe). Ciononostante, Hibern ate fornisce una funzionalità (lievemente sperimentale), che consente di mappare associazioni molti-a-molti e collezioni di valori su una tabella con una chiave surrogata.
L'elemento <idbag> vi permette di mappare una List (o una Collection) con la semantica del sacco ("bag").
<idbag name="lovers" table="LOVERS" lazy="true"> <collection-id column="ID" type="long"> <generator class="hilo"/> </collection-id> <key column="PERSON1"/> <many-to-many column="PERSON2" class="eg.Person" outer-join="true"/> </idbag>
Come potete vedere, un <idbag> ha un generatore di id sintetici essattamente come una classe di entità! Una chiave surrogata diversa viene assegnata ad ogni riga della collezione. Hibernate non fornisce alcun meccanismo per scoprire la chiave surrogata di una particolare riga, però.
Notate che le performance in aggiornamento di un <idbag> sono molto migliori di un <bag> normale! Hibernate può individuare chiavi individuali in maniera efficiente, e aggiornare o cancellarle individualmente esattamente come in una lista, una mappa o un insieme.
Nell'implementazione corrente, la strategia di generazione degli identificatori indicata con identity non viene supportata per gli identificatori di collezione <idbag>.
Una associazione bidirezionale consente la navigazione da entrambe le estremità dell'associazione. Vengono supportati due stili differenti di associazioni bidirezionali:
un insieme o un sacco ad una estremità, un valore singolo dall'altra
entrambe le estremità sono valorizzate con un set o un sacco
Tenete presente che HIbernate non supporta associazioni bidirezionali uno-a-molti con una collezione indicizzata (lista, mappa o array) come estremità "molti": dovete usare un mappaggio "set" o "bag".
Potete specificare una associazione bidirezionale molti-a-molti semplicemente mappando due associazioni molti-a-molti sulla stessa tabella di database e dichiarando una estremità come inverse (decidere quale è una scelta che sta a voi). Ecco un esempio di un'associazione bidirezionale molti-a-molti da una classe a se stessa (ogni categoria può avere molti elementi, ed ogni elemento può essere in molte categorie):
<class name="org.hibernate.auction.Category"> <id name="id" column="ID"/> ... <bag name="items" table="CATEGORY_ITEM" lazy="true"> <key column="CATEGORY_ID"/> <many-to-many class="org.hibernate.auction.Item" column="ITEM_ID"/> </bag> </class> <class name="org.hibernate.auction.Item"> <id name="id" column="ID"/> ... <!-- inverse end --> <bag name="categories" table="CATEGORY_ITEM" inverse="true" lazy="true"> <key column="ITEM_ID"/> <many-to-many class="org.hibernate.auction.Category" column="CATEGORY_ID"/> </bag> </class>
Dei cambiamenti fatto esclusivamente all'estremità "inversa" dell'associazione non vengono resi persistenti. Questo significa che Hibernate ha due rappresentazioni in memoria per ogni associazione bidirezionale: un collegamento da A a B e un altro collegamento da B ad A. Questa cosa è più facile da comprendere se pensate al modello ad oggetti Java e come creiamo una relazione molti-a-molti in Java:
category.getItems().add(item); // La categoria ora "sa" della relazione item.getCategories().add(category); // L'elemento ora "sa" della relazione session.update(item); // Nessun effetto, niente verrà salvato! session.update(category); // La relazione verrà salvata
Il lato non-inverso viene usato per salvare la rappresentazione in-memoria sul database. Otterremmo un INSERT/UPDATE non necessario e probabilmente anche una violazione di chiave esterna se entrambe le estremità scatenassero dei cambiamenti! La stessa cosa vale naturalmente per le associazioni bidirezionali uno-a-molti.
Potete mappare una associazione bidirezionale uno-a-molti mappandola sulle stesse colonne come una associazione molti-a-uno e dichiarando l'estremità "molti" come inverse="true".
<class name="eg.Parent"> <id name="id" column="id"/> .... <set name="children" inverse="true" lazy="true"> <key column="parent_id"/> <one-to-many class="eg.Child"/> </set> </class> <class name="eg.Child"> <id name="id" column="id"/> .... <many-to-one name="parent" class="eg.Parent" column="parent_id"/> </class>
Mappare una estremità di un'associazione con inverse="true" non condiziona il funzionamento delle cascate, si tratta di concetti differenti!
Ci sono due approcci possibili per mappare una associazione ternaria. Uno è usare elementi compositi (discusso più avanti). Un altro è di usare una mappa con un'associazione come indice:
<map name="contracts" lazy="true"> <key column="employer_id"/> <index-many-to-many column="employee_id" class="Employee"/> <one-to-many class="Contract"/> </map>
<map name="connections" lazy="true"> <key column="node1_id"/> <index-many-to-many column="node2_id" class="Node"/> <many-to-many column="connection_id" class="Connection"/> </map>
Gli elementi <many-to-any> e <index-many-to-any> permettono di utilizzare vere e proprie associazioni eterogenee. Questi elementi di mappaggio funzionano nello stesso modo ini cui funziona l'elemento <any>, e come questo dovrebbero essere usate raramente, se proprio devono esserlo.
Le sezioni precedenti sono abbastanza complesse, quindi vediamo un esempio. La classe seguente:
package eg; import java.util.Set; public class Parent { private long id; private Set children; public long getId() { return id; } private void setId(long id) { this.id=id; } private Set getChildren() { return children; } private void setChildren(Set children) { this.children=children; } .... .... }
ha una collezione di istanze di eg.Child. Se ogni figlio ha al più un genitore, il mappaggio più naturale è un'associazione uno-a-molti:
<hibernate-mapping> <class name="eg.Parent"> <id name="id"> <generator class="sequence"/> </id> <set name="children" lazy="true"> <key column="parent_id"/> <one-to-many class="eg.Child"/> </set> </class> <class name="eg.Child"> <id name="id"> <generator class="sequence"/> </id> <property name="name"/> </class> </hibernate-mapping>
Questo si mappa sulle seguenti definizioni di tabella:
create table parent ( id bigint not null primary key ) create table child ( id bigint not null primary key, name varchar(255), parent_id bigint ) alter table child add constraint childfk0 (parent_id) references parent
Se il genitore è obbligatorio, usate una associazione bidirezionale uno-a-molti:
<hibernate-mapping> <class name="eg.Parent"> <id name="id"> <generator class="sequence"/> </id> <set name="children" inverse="true" lazy="true"> <key column="parent_id"/> <one-to-many class="eg.Child"/> </set> </class> <class name="eg.Child"> <id name="id"> <generator class="sequence"/> </id> <property name="name"/> <many-to-one name="parent" class="eg.Parent" column="parent_id" not-null="true"/> </class> </hibernate-mapping>
Notate il vincolo NOT NULL:
create table parent ( id bigint not null primary key ) create table child ( id bigint not null primary key, name varchar(255), parent_id bigint not null ) alter table child add constraint childfk0 (parent_id) references parent
Dall'altro lato, se un figlio potesse avere genitori multipli, sarebbe appropriata una associazione molti-a-molti:
<hibernate-mapping> <class name="eg.Parent"> <id name="id"> <generator class="sequence"/> </id> <set name="children" lazy="true" table="childset"> <key column="parent_id"/> <many-to-many class="eg.Child" column="child_id"/> </set> </class> <class name="eg.Child"> <id name="id"> <generator class="sequence"/> </id> <property name="name"/> </class> </hibernate-mapping>
Definizioni delle tabelle:
create table parent ( id bigint not null primary key ) create table child ( id bigint not null primary key, name varchar(255) ) create table childset ( parent_id bigint not null, child_id bigint not null, primary key ( parent_id, child_id ) ) alter table childset add constraint childsetfk0 (parent_id) references parent alter table childset add constraint childsetfk1 (child_id) references child