NHibernate Mapping - <many-to-one/>
Next up for inspection is the <many-to-one/> element. This element is defined as:
<many-to-one name="PropertyName" (1) column="column_name" (2) class="ClassName" (3) cascade="all|none|save-update|delete" (4) fetch="join|select" (5) update="true|false" (6) insert="true|false" (7) property-ref="PropertyNameFromAssociatedClass" (8) access="field|property|nosetter|ClassName" (9) unique="true|false" (10) optimistic-lock="true|false" (11) not-found="ignore|exception" (12) />
Much of the attributes on this element are identical to the ones that I outlined in the post about <property/>. 1, 2, 3, 6, 7, 9 and 11 are identical, and I am not going to cover them.
4) cascade is interesting, it controls one of the more interesting NHibernate features, persistence by reachability.I outlined all the possible options in 2006, so I wouldn’t repeat them again.
5) fetch is really interesting. Let us take a look at an entity definition, and explore how modifying it can alter NHibernate’s behavior.
<class name="Post" table="Posts"> <id name="Id"> <generator class="identity"/> </id> <property name="Title" /> <many-to-one name="Blog" column="BlogId"/> </class>
And we have the following code to exercise NHibernate:
using (var session = sessionFactory.OpenSession()) using (var tx = session.BeginTransaction()) { var post = session.Get<Post>(1); Console.WriteLine(post.Title); tx.Commit(); }
This would result in the following SQL.
But interesting things happen when we start playing with the fetch attribute. Note that by default the fetch attribute is defaulting to “select”, so setting it to that value is merely making things explicit, but setting it to fetch=”join”, like this:
<many-to-one name="Blog" column="BlogId" fetch="join"/>
Would result in the following SQL:
We eagerly load the Blog association, in this case.
8) property-ref is a legacy feature, it is meant to allow you to create many-to-one associations when the association is not done on the primary key of the association. In general, I would strongly suggest avoiding it.
9) unique is relevant only if you use NHibernate to specify your schema. This would generate a unique constraint when we generate the DDL.
12) not-found is another legacy feature, it controls how NHibernate behaves when it finds an invalid foreign key. That is, a value that points to an entity that doesn’t exist. By default, this would trigger an error, as this generally indicate a problem with the database, but with legacy database, you can tell it to set the property value to null instead.
Comments
I've found a few problems when using property-ref and natural keys so I would heartily recommend adding a column for a surrogate key when using nhibernate. It all works pretty well with surrogate keys.
Sucks that I've had to use both property-ref and not-found ... stupid legacy dbs!
About not-found, is there a way to specify a different default value in case the foreign key is not found ? I've got a legacy database that has missing values, and doesn't allow nulls either.
Thanks for your series so far, it's good to see this.
Ayende, I have a question:
Let's say I have a 'Employee' - it has a many to one mapping to a Company
The Company in turn has a many to one to let's say a 'address'
Basically, if I set all these many -to-one's to 'fetch=join'
Will it propagate down when I get an employee ?
Meaning, when I ask for an employee it will join to company and company join to address in one call ?
Thanks
Yes, it would.
Miki,
For that, you would need a custom IUserType to manage that.
Ayende - I posted this to nhusers about using "fetch='join'" vs FetchMode.Eager
http://xrl.us/beoeep
Basically, setting fetch='join' has the desired effect, but setting FetchMode.Eager in my criteria doesn't have the same effect and I would expect that it does. I must be doing something slightly wrong.
Thanks - Luke
That is because there is a difference between Eager and Join.
You should use Join or Select vs. Eager or Lazy
Ayende, thanks for your response. The FetchMode enum defines "Eager" and "Join" to be equivalent. Using "FetchMode.Join" still does not use a join in the output SQL.
namespace NHibernate
{
}
Hi Ayende,
I am finding an issue with the fetch mode set to Join. I have two child collections in my parent class and fetch set to join on both my child classes in the mapping. If I create 2 children and save the mapping, when I reload, I have 4 in each collection. I understand that you will get 4 rows from the join, but I would have thought that NHiberante would have worked out the duplicate identities.
Parent Mapping: (The Children 1 and 2 just contains an Id)
<hibernate-mapping
<class
<id
<generator
<bag
<key>
<one-to-many
<bag
<key>
<one-to-many
The parent class:
using System.Collections.Generic;
namespace Domain
{
<child1 Children1 { get; set; }
<child2 Children2 { get; set; }
<child1();
<child2();
}
the program:
<parent();
I get that there are 4 children in each collection.
Am I using the wrong collection type or is this a known feature/bug in nhibernate?
This time with the mapping....I hope
<class name="Parent">
</class>
LukeB,
Please post this again in the mailing list, I'll look at it there.
Anthony
That is the expected behavior in this scenario if you are not using a set.
That is because NHibernate will use values from the database, and it has no way of knowing what you are actually intending to do with them.
@Ayende
Are there performance issues in having to use @property-ref for those kind of mappings ?
I'm dealing on a daily basis with a (horrible) legacy database and today I had this kind of association:
CATEGORY_A one-to-many CATEGORY_B and then
CATEGORY_B many-to-one CATEGORY_B_DATA
CATEGORY_A and B are using single table inheritance using a formula based discriminator.
The association between CATEGORY_B and CATEGORY_B_DATA is via @property-ref and it's set to use JOIN FETCHING.
If I load up a CATEGORY_B object, NHibernate will correctly issue a LEFT OUTER JOIN and will retrieve all the necessary attributes to rehydrate CATEGORY_B_DATA objects but then somehow I see further SELECT statements issued for each one of the CATEGORY_B_DATA that should have already been loaded using the JOIN FETCHING, those SELECT statements are constrained on the value of the @property-ref set on the many to one association.
Would using the @property-ref mess something in the identity map making NHibernate to reload again those same objects ?
I sometimes wonder if NHibernate is the best tool for totally screwed legacy databases....
Daniel
Daniel,
Those type of questions are best served in the NH Users mailing list
@Ayende,
with fetch="select" option, it issues seperate select command for each of the many-to-one association if it exists even though i am not using the many-to-one part. is that the intended behaviour?
i thought with fetch="select" option, separate select command would be issued only when many-to-one property is selected.
i am using nhiberntate 2.1.
cheers
k03123,
fetch and lazy are two separate aspects
does that mean lazy-loading is not available for many-to-one?
thanks ayende
many-to-one support lazy loading
my apologies ayende,
lazy="false" in my many-to-one class. no wonder i wasn't gettting expected results.
thanks
Comment preview