4
votes

I have a problem trying to model a many-to-one relationship in NHibernate, where the object on the 'one' side has a unique constraint on a column. The problem is as follows:

I have two tables, 'Person' and 'Country'. Each Person has one and only one Country associated with it. A Country can have many Persons (really! :)) and a Countries' Name is unique. The following is the mapping on the Person side:

<many-to-one Name="Country">
<column Name="CountryId"/>
</many-to-one>

On the Country side:

<property name="Name" unique="true">
<column name="Name" length="50">
</property>

Now in the database I have added a unique constraint on the Name column in the Country table. If I call Save() on a Person instance NHibernate just tries to do INSERTS, whereas I would expect it to check if a Country Name exists and use its ID in the CountryID column in the Person table. Instead, an exception is thrown that results from violation of the unique constraint in the database.

It seems to me Nibernate should have enough mapping metadata to do the right thing (or does the unique attribute on the property not ensure this?). Does anyone know how to do this or have a workaround?

Thanks,

Martijn

2

2 Answers

6
votes

You need to assign a Country instance to the Country property of the Person instance (not just set the ID). Something like:

Person p = new Person();
p.Country = session.Load<Country>(countryId);
session.Save(p);

Then NHibernate will know what to do. This will also not cause a DB hit to retrieve country, since the Load method will return a Country proxy and the only thing you're accessing is the Country instance's ID.

0
votes

I had a similar requirement and solved it using SaveOrUpdateCopy.

Lets say you have two different People objects, and each has a reference to a different Country object. As long as the Country IDs are the same, you won't get an exception and only 1 Country will be in the database.

The only thing with this approach is that you will need to assign an ID to your Country objects before calling SaveOrUpdateCopy.