Ayende @ Rahien

Hi!
My name is Ayende Rahien
Founder of Hibernating Rhinos LTD and RavenDB.
You can reach me by phone or email:

ayende@ayende.com

+972 52-548-6969

@

Posts: 5,947 | Comments: 44,541

filter by tags archive

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.

image 

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:

image

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

pb
pb

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.

huey

Sucks that I've had to use both property-ref and not-found ... stupid legacy dbs!

Miki Watts

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.

Steve

Thanks for your series so far, it's good to see this.

Steve

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

Ayende Rahien

Miki,

For that, you would need a custom IUserType to manage that.

LukeB

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

Ayende Rahien

That is because there is a difference between Eager and Join.

You should use Join or Select vs. Eager or Lazy

LukeB

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

{

public enum FetchMode

{

    // Summary:

    //     Default to the setting configured in the mapping file.

    Default=0,

    Lazy=1,

    //

    // Summary:

    //     Fetch eagerly, using a separate select. Equivalent to fetch="select" (and

    //     outer-join="false")

    Select=1,

    //

    // Summary:

    //     Fetch using an outer join. Equivalent to fetch="join" (and outer-join="true")

    Join=2,

    Eager=2,

}

}

Anthony Dewhirst

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

<one-to-many

<bag

<one-to-many

The parent class:

using System.Collections.Generic;

namespace Domain

{

public class Parent : AbstractDomainObject

{

    #region Core Properties


    public virtual IList

<child1 Children1 { get; set; }

    public virtual IList

<child2 Children2 { get; set; }

    #endregion


    #region Constructors


    public Parent()

    {

        Children1 = new List

<child1();

        Children2 = new List

<child2();

    }


    #endregion

}

}

the program:

    static void Main(string[] args)

    {

        Configuration cfg = new Configuration();

        cfg.Configure();


        new SchemaExport(cfg).Drop(false, true);

        new SchemaExport(cfg).Create(false, true);


        ISessionFactory sessionFactory = cfg.BuildSessionFactory();


        using (var session = sessionFactory.OpenSession())

        using (var tx = session.BeginTransaction())

        {

            var parent = new Parent();

            parent.Children1.Add(new Child1());

            parent.Children1.Add(new Child1());


            parent.Children2.Add(new Child2());

            parent.Children2.Add(new Child2());


            session.Save(parent);


            tx.Commit();

        }


        using (var session = sessionFactory.OpenSession())

        using (var tx = session.BeginTransaction())

        {

            var parents = session.CreateCriteria(typeof (Parent)).List

<parent();

            Console.WriteLine("There are {0} children1", parents[0].Children1.Count);

            Console.WriteLine("There are {0} children2", parents[0].Children2.Count);

        }


        Console.ReadKey();

    }

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?

Anthony Dewhirst

This time with the mapping....I hope

<id name="Id">

  <generator class="native"/>

</id>


<bag name="Children1" cascade="all-delete-orphan" lazy="false" generic="true" fetch="join">

  <key column="ParentId"></key>

  <one-to-many class="Child1" />

</bag>


<bag name="Children2" cascade="all-delete-orphan" lazy="false" generic="true" fetch="join">

  <key column="ParentId"></key>

  <one-to-many class="Child2" />

</bag>

Ayende Rahien

LukeB,

Please post this again in the mailing list, I'll look at it there.

Ayende Rahien

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.

Daniel Fernandes

@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:

CATEGORYA one-to-many CATEGORYB and then

CATEGORYB many-to-one CATEGORYB_DATA

CATEGORY_A and B are using single table inheritance using a formula based discriminator.

The association between CATEGORYB and CATEGORYB_DATA is via @property-ref and it's set to use JOIN FETCHING.

If I load up a CATEGORYB object, NHibernate will correctly issue a LEFT OUTER JOIN and will retrieve all the necessary attributes to rehydrate CATEGORYB_DATA objects but then somehow I see further SELECT statements issued for each one of the CATEGORYBDATA 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

Ayende Rahien

Daniel,

Those type of questions are best served in the NH Users mailing list

k03123

@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

Ayende Rahien

k03123,

fetch and lazy are two separate aspects

k03123

does that mean lazy-loading is not available for many-to-one?

thanks ayende

Ayende Rahien

many-to-one support lazy loading

k03123

my apologies ayende,

lazy="false" in my many-to-one class. no wonder i wasn't gettting expected results.

thanks

Comment preview

Comments have been closed on this topic.

FUTURE POSTS

No future posts left, oh my!

RECENT SERIES

  1. RavenDB Sharding (3):
    22 May 2015 - Adding a new shard to an existing cluster, splitting the shard
  2. The RavenDB Comic Strip (2):
    20 May 2015 - Part II – a team in trouble!
  3. Challenge (45):
    28 Apr 2015 - What is the meaning of this change?
  4. Interview question (2):
    30 Mar 2015 - fix the index
  5. Excerpts from the RavenDB Performance team report (20):
    20 Feb 2015 - Optimizing Compare – The circle of life (a post-mortem)
View all series

RECENT COMMENTS

Syndication

Main feed Feed Stats
Comments feed   Comments Feed Stats