NHibernate Mappings
In this post, I will talk about NHibernate mappings. Let's start from the data model:
What we can see here are 4 types of relationships:
-
A one-to-one, from table A to table E;
-
A many-to-one, from table B to table C;
-
A one-to-many, from table C to table D;
-
A many-to-one, from table D to table C.
Also, note this:
-
All tables, except A, D and E, have composite primary keys; this is the most generic case;
-
All tables, except B_C, have a Text column;
-
Primary keys are always named <tablename><number>, for example, A1, A2, etc;
-
Foreign keys have the same name as the referring primary key.
Now, let's see the classes that map these tables and relationships:
And the source code is:
using
System;using
System.Collections.Generic;namespace NHibernateTest{
[Serializable] public class A{
public virtual Int32 Id { get; set; }public virtual E E { get; set; } public virtual String Text { get; set; }public override Boolean Equals(Object obj){
if (!(obj is A)){
return (false);}
if (Object.ReferenceEquals(this, obj) == true){
return (true);}
A other = obj as A;return(this.Id == other.Id);}
public override Int32 GetHashCode(){
return (this.Id);}
}
}
using System; using System.Collections.Generic;namespace NHibernateTest
{
[Serializable] public class B{
public class BKey{
public virtual Int32 B1 { get; set; }public virtual Int32 B2 { get; set; }
public override Boolean Equals(Object obj){
if (!(obj is BKey)){
return (false);}
if (Object.ReferenceEquals(this, obj) == true){
return (true);}
BKey other = obj as BKey;return ((this.B1 == other.B1) && (this.B2 == other.B2));}
public override Int32 GetHashCode(){
return (this.B1 + (1000 * this.B2));}
}
public virtual BKey Id { get; set; }public virtual String Text { get; set; } public virtual ISet<C> C { get; set; }public override Boolean Equals(Object obj){
if (!(obj is B)){
return (false);}
if (Object.ReferenceEquals(this, obj) == true){
return (true);}
B other = obj as B;return (Object.Equals(this.Id, other.Id) == true);}
public override Int32 GetHashCode(){
return (this.Id != null ? this.Id.GetHashCode() : 0);}
}
}
using
System;using
System.Collections.Generic;namespace NHibernateTest{
[Serializable] public class C{
public class CKey{
public virtual Int32 C1 { get; set; }public virtual Int32 C2 { get; set; }
public override Boolean Equals(Object obj){
if (!(obj is CKey)){
return (false);}
if (Object.ReferenceEquals(this, obj) == true){
return (true);}
CKey other = obj as CKey;return ((this.C1 == other.C1) && (this.C2 == other.C2));}
public override Int32 GetHashCode(){
return (this.C1 + (1000 * this.C2));}
}
public virtual CKey Id { get; set; }public virtual String Text { get; set; } public virtual ISet<B> B { get; set; }public virtual ISet<D> D { get; set; }
public override Boolean Equals(Object obj){
if (!(obj is C)){
return (false);}
if (Object.ReferenceEquals(this, obj) == true){
return (true);}
C other = obj as C;return (Object.Equals(this.Id, other.Id) == true);}
public override Int32 GetHashCode(){
return (this.Id != null ? this.Id.GetHashCode() : 0);}
}
}
using
System;using
System.Collections.Generic;namespace NHibernateTest{
[Serializable] public class D{
public virtual Int32 Id { get; set; }public virtual C C { get; set; }
public virtual String Text { get; set; }}
}
using
System;using
System.Collections.Generic;namespace NHibernateTest{
[Serializable] public class E{
public virtual A A { get; set; }public virtual String Text { get; set; }}
}
As you can see:-
There is no class to map table B_C, this is because this is pure mapping table, it contains no additional attributes other than the foreign keys;
-
The id for class E is a property of type A;
-
No base class;
-
Methods Equals and GetHashCode are always implemented;
-
Collections are ISet<T>, which means they cannot hold equal objects;
-
Properties are auto properties and always have public setters;
-
Id classes for mapping composite keys are implemented as internal.
Now, here are the mappings:
For class A:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="NHibernateTest" assembly="NHibernateTest" auto-import="true" default-access="property"> <class name="A" table="`A`"><
id name="Id" column="`A1`"> <generator class="foreign"><
param name="property">E</param> </generator></
id> <property name="Text" column="`TEXT`"/><
one-to-one name="E" class="E" /> </class></
hibernate-mapping>For class B:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="NHibernateTest" assembly="NHibernateTest" auto-import="true" default-access="property"><
class name="B" table="`B`"> <composite-id class="B+BKey" name="Id"><
key-property column="`B1`" name="B1"/> <key-property column="`B2`" name="B2"/> </composite-id><
property name="Text" column="`TEXT`"/> <set name="C" table="`B_C`" fetch="join" cascade="save-update" outer-join="true"><
key> <column name="`B1`"/><
column name="`B2`"/> </key><
many-to-many class="C" fetch="join" outer-join="true"> <column name="`C1`"/><
column name="`C2`"/> </many-to-many></
set> </class></
hibernate-mapping>For class C:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="NHibernateTest" assembly="NHibernateTest" auto-import="true" default-access="property"><
class name="C" table="`C`"> <composite-id class="C+CKey" name="Id"><
key-property column="`C1`" name="C1"/> <key-property column="`C2`" name="C2"/></
composite-id> <property name="Text" column="`TEXT`" /><
set name="B" table="`B_C`" fetch="join" cascade="save-update" outer-join="true"> <key><
column name="`C1`"/> <column name="`C2`"/></
key> <many-to-many class="B" fetch="join"> <column name="`B1`"/><
column name="`B2`"/> </many-to-many></
set> <set name="D" inverse="true" cascade="all-delete-orphan" outer-join="true"><
key> <column name="`C1`"/><
column name="`C2`"/> </key><
one-to-many class="D"/> </set></
class></hibernate-mapping> For class D:<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="NHibernateTest" assembly="NHibernateTest" auto-import="true" default-access="property"><
class name="D" table="`D`"> <id name="Id" column="`D1`"><
generator class="assigned"/> </id><
property name="Text" column="`TEXT`"/> <many-to-one name="C"><
column name="`C1`"/> <column name="`C2`"/></
many-to-one> </class></
hibernate-mapping>And finally, for class E:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="NHibernateTest" assembly="NHibernateTest" auto-import="true" default-access="property"><
class name="E" table="`E`"> <id type="System.Int32"><
column name="`A1`"/> <generator class="assigned"/></
id> <one-to-one name="A" class="A"/><
property name="Text" column="`TEXT`"/> </class></
hibernate-mapping>The notes for the mappings are:
- Table and column names are enclosed in `;
- Collections are sets, not lists, idbags or bags;
- Primary keys are manually assigned;
- Properties are directly used to access values;
- Collections always use outer-join.
Hope this is useful to somebody!