NHibernate Mappings

In this post, I will talk about NHibernate mappings. Let's start from the data model:

Tables

What we can see here are 4 types of relationships:

  1. one-to-one, from table A to table E;
  2. A many-to-one, from table B to table C;
  3. A one-to-many, from table C to table D;
  4. A many-to-one, from table D to table C.

Also, note this:

  1. All tables, except A, D and E, have composite primary keys; this is the most generic case;
  2. All tables, except B_C, have a Text column;
  3. Primary keys are always named <tablename><number>, for example, A1, A2, etc;
  4. Foreign keys have the same name as the referring primary key.

Now, let's see the classes that map these tables and relationships:

Classes

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:

  1. 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;
  2. The id for class E is a property of type A;
  3. No base class;
  4. Methods Equals and GetHashCode are always implemented;
  5. Collections are ISet<T>, which means they cannot hold equal objects;
  6. Properties are auto properties and always have public setters;
  7. 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="joincascade="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:

  1. Table and column names are enclosed in `;
  2. Collections are sets, not lists, idbags or bags; 
  3. Primary keys are manually assigned;
  4. Properties are directly used to access values;
  5. Collections always use outer-join.

Hope this is useful to somebody!

                             

4 Comments

  • Is there any tool to automate the generation of mapping files?

  • There are some: MyGeneration (free, template-based), CodeSmith (comercial, also template-based) and NConstruct and NConstruct Lite (the latter is free). There is also the NHibernate Plugin for Visual Studio, which is a free project hosted on SourceForge, but it only supports SQL Server, and I think it's development has stopped). All of these have problems, I will talk about them in a future post.

  • Will it only work with SET? I need the property as IList, so can only use BAG or LIST...

    Help will be really appreciated!!!

  • Yes, but you need an extra column for the index, List requires it.

Comments have been disabled for this content.