views:

2135

answers:

5

Hi all,

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

A: 

Have you tried using SaveOrUpdate() ?

George Stocker
A: 

Yep, but that doesnt make a difference. Thanks, though.

I guess this should really be a comment to your answer but I have not enough kudos to do that. Also I cant edit my own spelling mistake in the question's title. I should probably register as a user :).

+4  A: 

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.

Sean Carpenter
A: 

I basically have the same scenario. But I think what Martin and I are both after is this:

Is it possible to get NHibernate to look at the Name property of the Country entity, check if it exists, and if it does then associate the Person with the correct country without manually having to check and associate? In my case the Id property of the Country entity would be set to NHibernates "unsaved value".

Person p = new Person();
p.Country = new Country { Name = "SomeExistingCountryName" }
session.Save(p);
Kristoffer Ahl
No, this isn't possible. You could retrieve the country by name using a query (HQL or ICriteria) and then set the Country property to that instance, but the Country property needs to be set to an instance that NHibernate is tracking in order for this to work.
Sean Carpenter
Thanks for the info Sean!
Kristoffer Ahl
A: 

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.

Cocowalla