NHibernate One-To-One Mapping

marcusstarnes picture marcusstarnes · Mar 3, 2011 · Viewed 24.2k times · Source

I'm new to NHibernate so have had limited exposure to mappings etc so far, and I've just hit a scenario which I need some help with.

I have 2 tables:

Reviews TaggedReviews

I have 2 classes that look like this (I've excluded non-important properties for brevity):

Review

public virtual int ReviewId { get; set; }
public virtual TaggedReview TaggedReview { get; set; }
public virtual string Title { get; set; }
public virtual string Descrip { get; set; }

TaggedReview

public virtual int ReviewId { get; set; }
public virtual Review Review { get; set; }
public virtual string TaggedReviewDescrip { get; set; }

My NHibernate XML mapping files for these tables/classes are currently as follows (edited for brevity):

Review.hbm.xml

<class xmlns="urn:nhibernate-mapping-2.2" name="Review" table="Reviews">
    <id name="ReviewId" unsaved-value="0">
        <column name="ReviewId"></column>
        <generator class="native" />
    </id>

    <property name="Title" not-null="true" />
    <property name="Descrip" not-null="true" />
    <one-to-one name="TaggedReview" class="TaggedReview" /> <!-- This is probably very wrong?! -->
</class>

TaggedReview.hbm.xml

<class xmlns="urn:nhibernate-mapping-2.2" name="TaggedReview" table="TaggedReviews">
    <id name="ReviewId">
        <column name="ReviewId"></column>
        <generator class="native"/>
    </id>
    <one-to-one name="Review" class="Review" /> <!-- This is probably very wrong?! -->
    <property name="TaggedReviewDescrip" not-null="true" />
</class>

The property 'ReviewId' is the PK for the 'Reviews' table. It's a FK in the 'TaggedReviews' table.

There is ALWAYS 1 record/row in the TaggedReviews table for every record/row in the Reviews table - no more, no less.

I was also unsure about the 'TaggedReview' class as that table has NO PK, just a FK for the Reviews table (ReviewId), but it looked like the mapping file needed an id element in there, so wasn't sure about this either!

Can anyone please advise how to best configure this mapping?

Answer

Florian Lim picture Florian Lim · Mar 3, 2011

Ayende has a good explanation for one-to-one mappings here.

In your case the mappings should probably look like this:

Review.hbm.xml

<class xmlns="urn:nhibernate-mapping-2.2" name="Review" table="Reviews">
    <id name="ReviewId" unsaved-value="0">
        <column name="ReviewId"></column>
        <generator class="native" />
    </id>

    <property name="Title" not-null="true" />
    <property name="Descrip" not-null="true" />
    <one-to-one name="TaggedReview"
                      constrained="true"
                      foreign-key="none" 
                      class="TaggedReview" /> 
<!-- foreign-key="none", to prevent circular reference at insert -->
</class>

TaggedReview.hbm.xml

You will most likely need a primary key in the table, because you can't use the same column for the key and the foreign key.

<class xmlns="urn:nhibernate-mapping-2.2" name="TaggedReview" table="TaggedReviews">
    <id name="SomeOtherId">
        <column name="SomeOtherId"></column>
        <generator class="native"/>
    </id>
    <many-to-one name="Review" 
                 unique="true"
                 class="Review"> <!-- Use many-to-one for a foreign key -->
      <column name="ReviewId" />
    </many-to-one>
    <property name="TaggedReviewDescrip" not-null="true" />
</class>

If you cannot or do not want to change the db, you can take a look at NHibernate mapping - one-to-one (or one-to-zero).