Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do this Unidirectional NHibernate one-to-one mapping?

This is a problem of unidirectional one-to-one mapping in NHibernate.

Student.cs

public class Student
{
    public int ID { get; set; }
    public int Roll { get; set; }
    public int RegNo { get; set; }
    public string Name { get; set; }

    public StudentDetail StudentDetail { get; set; }
}

StudentDetail.cs

public class StudentDetail
{
    public int ID { get; set; }

    public string Father { get; set; }
    public string Mother { get; set; }
}

How can I map these classes (how do the hbm mapping files look like) to the following case of one-to-one relationship?

alt text

Please have a look at the classes and the table very carefully.

Where can I put the <many-to-one> tag in Student.hbm.xml or StudentDetail.hbm.xml? If I put it in Student.hbm.xml, how can I map the column StudentDetail.StudentID, coz it is in a different table?

So this mapping:

<class name="Student" table="Student">
    <id name="ID" column="ID">
      <generator class="native"/>
    </id>

    .......

    <many-to-one class="StudentDetail" name="StudentDetail" column="StudentID" unique="true" cascade="all" />
  </class>

generates the following exception:

{"Invalid column name 'StudentID'."}

On the other hand <many-to-one> can't be placed in StudentDetail.hbm.xml. Coz, StudentDetail.cs doesn't contain any property of type Student.

Can I use <one-to-one>-tag? If yes where should I place it, in Student.cs or StudentDetail.cs? And how should I configure it?

like image 704
user366312 Avatar asked Jun 07 '10 16:06

user366312


2 Answers

Case #1:

In Student...

<one-to-one name="StudentDetail" 
            cascade="save-update,delete" 
            property-ref="Student" />

In StudentDetail...

<many-to-one name="Student" 
             column="StudentID" 
             unique="true" 
             cascade="none" />

Note that you'll have to have a property in your StudentDetail class that refers to a Student oobject (called Student). Also, your cascades might be different depending on your usage. You most likely want the delete cascade in there, though.

The unique="true" ensures the one-to-one mapping on the StudentDetail side.

Case #2:

Just exchange the two mappings, making sure you change the property names to the opposite class.

Look here for more info: http://nhforge.org/blogs/nhibernate/archive/2009/04/19/nhibernate-mapping-lt-one-to-one-gt.aspx

like image 105
Chris Avatar answered Nov 06 '22 03:11

Chris


You can map it as a one-to-many, with the collection property hidden and only its first element publicly exposed:

public class Student
{
    public virtual int ID { get; set; }
    public virtual int Roll { get; set; }
    public virtual int RegNo { get; set; }
    public virtual string Name { get; set; }

    protected virtual IList<StudentDetail> StudentDetails { get; set; }

    public virtual StudentDetail StudentDetail
    {
        get
        {
            if (StudentDetails.Count == 0) return null;
            return StudentDetails[0];
        }
        set
        {
            if (StudentDetails.Count != 0) throw new Exception();
            StudentDetails.Add(value);
            value.Student = this;
        }
    }
}

You could handle the setter better than this - the point is to make sure you don't add multiple rows to the one-to-many. Obviously in this, StudentDetails is mapped but StudentDetail isn't in your .hbm.xml or Fluent mappings.

like image 2
David M Avatar answered Nov 06 '22 04:11

David M