NHibernate Part 5: Mapping techniques for aggregation – Many-To-One and Many-To-Many mapping

October 21, 2006

Updated code available for NHibernate 2.0.1.GA here.
The toolingset used is Visual Studio 2008 and Sql Server 2008

The final part in my experimentation series on NHibernate which discusses aggregation.

In this post I will discuss the following forms of aggregation:

  • many-to-one: an object is used by many other objects
  • many-to-many: an object is itself used by many other objects and vice versa

Multiplicity of aggregation: Many to One mapping

The Code

We allready used this tag when making One-to-One and One-to-Many relations bidirectional. In this post I will discuss using Many-to-One relations directly.

A many to one relation occurs when one object can be used by many other objects. So the many objects refer to the same one object.

Using the <many-to-one> mapping tag

On the code side:
A many to one mapping in code is simply many objects all referring to one object.

public class UParking
{
   // Just the usual stuff
}

public class UCar
{
   // Just the usual stuff

   // Members for the many to one mapping
   private UParking place;

   public UParking Place 
   {
      get { return this.place; }
      set { this.place = value; }
   }
}

On the mapping side:

The mapping for the UParking class needs no special tags, we just use the usual ones.

The UCar class has the usual tags too, except for the many-to-one tag:

<many-to-one name="Place" 
   class="UniDirectional.Traffic.UParking, 
              NHibernateManyToOne" 
   column="ParkingId" cascade="all"/>


What we are saying here is the following:
The Place property is a many to one property to an object of class UParking. The values for the object can be found by looking for an entry in the table for UParking objets with an id equal to the value in column ParkingId of this table.

On the database side:

CREATE TABLE `car` (
   `Id` int(10) unsigned NOT NULL auto_increment,
   `Manufacturer` varchar(45) NOT NULL default '',
   `ParkingId` int(10) unsigned NOT NULL default '0',
   PRIMARY KEY  (`Id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;


CREATE TABLE `parking` (
   `Id` int(10) unsigned NOT NULL auto_increment,
   `Name` varchar(45) NOT NULL default '',
   PRIMARY KEY  (`Id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

To make the mapping bidirectional:

We allready saw this mapping in the other direction when discussing aggregation with one-to-one and one-to-many multiplicity.

Multiplicity of aggregation: Many to Many mapping

The Code

The final entry for aggregation is the many to many mapping.

An object uses several objects of another type, and this latter object references several objects of the first type.

Using the <many-to-many> mapping tag

On the code side:

public class UCourse
{
   private int id;
   private string teacher;

   public int Id 
   {
      get { return this.id; }
      set { this.id = value; }
   }

   public string Teacher 
   {
      get { return this.teacher; }
      set { this.teacher = value; }
   }
}

public class UStudent
{
   private int id;
   private string name;
   private IList orderList;


   public int Id 
   {
      get { return this.id; }
      set { this.id = value; }
   }

   public string Name 
   {
      get { return this.name; }
      set { this.name = value; }
   }

   public IList CourseList
   {
      get { return this.orderList; }
      set { this.orderList = value; }
   }
}

On the mapping side:

The mapping for the UCourse class needs no special tags, we just use the usual ones.

The UStudent class has the usual tags too, except for the many-to-many tag:

<bag name="CourseList" table="student_course" cascade="all">
   <key column="StudentId"/>
   <many-to-many column="CourseId" 
      class="UniDirectional.UCourse, NHibernateManyToMany"/>
</bag>


What we are saying here is the following:
The CourseListproperty is a many to many property to a bag collection of objects with type UCourse. The values for the objects in this collection can be found by looking for entries in the table for UCourse objects with an id equal to the values in column CourseId of the student_course table with a value of StudentId equal to this objects id.

On the database side:

CREATE TABLE `student` (
   `Id` int(10) unsigned NOT NULL auto_increment,
   `Name` varchar(45) NOT NULL default '',
   PRIMARY KEY  (`Id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;


CREATE TABLE `course` (
   `Id` int(10) unsigned NOT NULL auto_increment,
   `Teacher` varchar(45) NOT NULL default '',
   PRIMARY KEY  (`Id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;


CREATE TABLE `student_course` (
   `StudentId` int(10) unsigned NOT NULL default '0',
   `CourseId` int(10) unsigned NOT NULL default '0',
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

As is always the case for many to many mappings in relational databases, we need a third table which provides the crossreferences for the many to many relationship.

On the usage site:

Using a many to many relationship goes like this:

UniDirectional.UCourse courseOne = 
   new UniDirectional.UCourse();
courseOne.Teacher = "TeacherOfCourseOne";
UniDirectional.UCourse courseTwo = 
   new UniDirectional.UCourse();
courseTwo.Teacher = "TeacherOfCourseTwo";
UniDirectional.UCourse courseThree = 
   new UniDirectional.UCourse();
courseThree.Teacher = "TeacherOfCourseThree";
ArrayList courseListOne = new ArrayList();
courseListOne.Add(courseOne);
courseListOne.Add(courseTwo);
ArrayList courseListTwo = new ArrayList();
courseListTwo.Add(courseOne);
courseListTwo.Add(courseThree);

UniDirectional.UStudent studentOne = 
   new UniDirectional.UStudent();
studentOne.Name = "StudentOne";
studentOne.CourseList = (IList)courseListOne;
UniDirectional.UStudent studentTwo = 
   new UniDirectional.UStudent();
studentTwo.Name = "StudentTwo";
studentTwo.CourseList = (IList)courseListTwo;

session.Save(studentOne);
session.Save(studentTwo);

As you can see it is all very straightforward. The difference with a one to many mapping is that one course can be used in the course collection for several students.

To make the mapping bidirectional:

To make the mapping bidirectional you must specify a <many-to-many> tag in the other mapping file too.

<bag name="StudentList" table="student_course" cascade="all">
   <key column="CourseId"/>
   <many-to-many column="StudentId" 
      class="BiDirectional.BStudent, NHibernateManyToMany"/>
</bag>

What’s next?

Next in this series is how to map inheritance.

Updates

21 October 2006: original version

About these ads

18 Responses to “NHibernate Part 5: Mapping techniques for aggregation – Many-To-One and Many-To-Many mapping”

  1. Rafael Rodrigues Says:

    What if the association class also has some usable fields other than the PK’s? How will I map/use them?

    Thanks, nice art.


  2. I’m not sure what you mean.

    Are you talking about many-to-many aggregation in which the association table (“student_course” in the above example) does have some other fields?

    If that is what you mean, you will have a third class representing this association table. The other two classes will have respective a many-to-one and one-to-mant relationship with this third class.

  3. Rafael Rodrigues Says:

    Yes, that is what I meant. My bad, sorry.
    You are right, that was the solution i’ve figured.
    But, now there is another issue:
    For what I know about DB rules, in the student_course table, both FK’s should also be one composite PK.
    In that case, nhibernate won’t accept the many-to-one from this to each parent table. What you suggest, is maybe better sacrifice normalization and turn them into non-PK?
    Thanks !


  4. I have not tried that yet, but if this doesn’t work you do not have much choice then to sacrifice your normalization.

    But I will try it and post my findings as soon as I can.

  5. Rafael Rodrigues Says:

    I had some progress on that. I mapped the table with using 2 . Gives you some extra work because you must override Equals() and GetHashCode(). So far it works fine. Tell me if you want details to publish here. Thank you a lot !

  6. Rafael Rodrigues Says:

    Oops, it doesn’t show html-like tags. The last post should have :
    “… mapped the table with ‘composite-id’ using 2 ‘key-many-to-one’.”

  7. Tim Norris Says:

    Have you tried to delete a child from a parent in a many-to-many relationship? I have been having problems.

    It would be good if you could try this out and give an example.

    Thanks.


  8. Unfortunately I do not have time at the moment for further experimentation with NHibernate. I do hope however to be able to further eperiment somewhere in the future and etend these articles with my experiences with performing CRUD operations with these mappings …

  9. Roberto Nunes Says:

    Hi,
    Analysing the many-to-many mapping, I have one question.
    If the table student_course have one column like year, how do mapp the relationship?

    CREATE TABLE `student_course` (
    `StudentId` int(10) unsigned NOT NULL default ‘0’,
    `CourseId` int(10) unsigned NOT NULL default ‘0’,
    `Year` int(4) NOT NULL)


  10. Hi Roberto,

    See my answer to the first comment of Rafael Rodrigues.


  11. Hi there, thanks a lot for this!

  12. Creasy Says:

    Hi serge desmedt,

    I’ve read all five nHibernate tutorials and I finally understood how to use the mapping technique.

    This is the best tutorial I’ve ever read about the aggregation in nHibernate.

    Thank you very much, serge.

    P.S.: Will you create any sample on how to use in nHibernate by any chance in the future? :)

  13. Creasy Says:

    oops,

    Will you create any sample on how to use “joined-subclass” in nHibernate by any chance in the future?


  14. Hi Creasy,

    I started these tutorials because at that time we where preparing for a project which could use NHibernate.

    In the mean time that project got cancelled and I drifted away from NHibernate. As you can see from my last posts I am currently investigating Workflow Foundation and even that will probably be quiet for some time as it is a very busy time at work.

    But maybe in the future…

    Anyway, thanks a lot for the positive feedback

    Serge

  15. Paulo Says:

    Muito bom!!!!

    Seu artigo me ajudou muito!!!

    Thank you very much!!!


  16. After reading through this article, I feel that I need more information on the topic. Can you suggest some more resources please?

  17. Pierce Says:

    Thanks!

    Just made my friday morning go smoothly – just the info i needed!


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: