Introduzione
Nel post [2] relativo ad NHibernate, avevamo visto come
scrivere file di mapping per legare due classi con una relazione uno-a-molti.
Nel caso specifico, lo riassumo in breve, avevamo una classe
HockeyPlayer ed una classe Faults: ad
un'istanza della prima, possono corrispondere più istanze della seconda. Senza
andare troppo nel dettaglio, più che altro per non ripetere cose già dette,
riporto qui sotto il mapping della classe
HockeyPlayer:
<class name='HockeyPlayer' table='HockeyPlayers'>
<id name='ID' column='ID' type='Int32' length='4' unsaved-value="0">
<generator class='identity' />
</id>
<property name='Name' type='String' column='Name' length='50'/>
<property name='Height' type='Int32' column='Height' length='4'/>
<property name='Weight' type='Int32' column='Weight' length='4'/>
<property name='Number' type='Int32' column='Number' length='4'/>
<bag name="Faults" cascade="all" lazy="false" inverse="true">
<key column="IDPlayer" />
<one-to-many class="Fault, HockeyObject" />
</bag>
</class>
Detto questo, avevamo detto che anche la classe Faults deve
essere mappata opportunamente. Quello che mi interessa far ricordare (e
ricordare a me stesso) è che nella classe Fault deve esserci un
riferimento all'istanza HockeyPlayer a cui appartiene il Fault
stesso. Di conseguenza:
<class name='Fault' table='Faults'>
<id name='ID' column='ID' type='Int32' length='4' unsaved-value="0">
<generator class='identity' />
</id>
<property name='Reason' type='String' column='Reason' length='50'/>
<property name='When' type='DateTime' column='[When]' length='4'/>
<property name='FaultGravity' type='Int32' column='Gravity' length='4'/>
<property name='FaultConsequence' type='Int32' column='Consequence' length='4'/>
<many-to-one name="HockeyPlayer" column="IDPlayer"
class="HockeyPlayer, HockeyObject" cascade="none" />
</class>
La cosa interessante è il parametro cascade contenuto nel
tag many-to-one. Ricordiamoci sempre di metterlo a
none: se non lo facciamo, succede una cosa strana ed un po'
pericolosa. Se attraverso la classe DataProvider descritta qua, cancelliamo un
oggetto Fault, in realtà cancelleremo anche il suo
HockeyPlayer di appartenenza. Questo non va affatto bene: il
fatto di voler cancellare un Fault non significa affatto che vogliamo cancellare
anche il giocatore! D'altra parte, se cancelliamo un oggetto
Fault, NHibernate deve prima cancellare eventuali record in altre tabelle che lo
usano come foreign-key, ed è proprio quello che accade! Il tutto, teniamolo presente, in modo
completamente trasparente rispetto al nostro data access layer: tutto è
stabilito nei files di mapping, e non c'è nulla di cablato dentro il codice.
Ed una relazione uno-ad-uno?
Ho avuto un'altra necessità
e per poterla descrivere qua, sono partito sempre dalla mia sample application.
Ho uno HockeyPlayer e la proprietà Faults,
relazionata uno-a-molti. Espandiamo lo scenario, aggiungendo la classe
Squad esposta dalla classe HockeyPlayer. Ad
ogni istanza di giocatore, esiste una ed al massima una istanza della squadra.
La relazione questa volta è uno-a-uno. Come la si esprime nei files di mappings?
Non è per nulla complicato, soprattutto alla luce di quello che abbiamo già
visto precedentemente.
<one-to-one name="Squadron" class="Squad"
property-ref="HockeyPlayer" cascade="all" />
Il tag one-to-one deve essere
aggiunto alla definizione della classe HockeyPlayer. Anche in
questo caso, abbiamo la proprietà name che indica il nome della proprietà esposta
dalla classe HockeyPlayer. La proprietà class indica il nome completo della classe. La
proprietà property-ref indica il
nome della proprietà sull'oggetto figlio che fa riferimento all'oggetto padre.
Ovviamente, ho dovuto modificare l'implementazione della classe
HockeyPlayer, aggiungendo la nuova proprietà:
// Field privato
private Squad squadron;
// Proprietà pubblica
public Squad Squadron
{
get { return squadron; }
set { squadron = value; NotifyPropertyChanged("Squadron"); }
}
Come ultimissima cosa, vediamo come scrivere nel file Mappings.hbm.xml
il mapping per la nuova classe Squad:
<class name='Squad' table='Squads'>
<id name='ID' column='ID' type='Int32' length='4' unsaved-value="0">
<generator class='identity' />
</id>
<property name='Name' type='String' column='Name' length='50'/>
<property name='City' type='String' column='City' length='50'/>
<many-to-one name="HockeyPlayer" class="HockeyPlayer"
column="IDPlayer" unique="true" cascade="none" />
</class>
La classe Squad è molto semplice: espone due
stringhe, una per il nome della squadra e l'altra per la città. La cosa
interessante da far notare è che anche in questo caso abbiamo dovuto mettere il
tag many-to-one per indicare il
riferimento al padre: tra tutte le proprietà indicate in questo tag, la più
interessante è sicuramente column,
che indica qual'è il nome del campo che deve contenere il legame tra
HockeyPlayer e la Squad corrispondente.