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!