Lazy loading not working for many-to-one relationship when mapping to a non-key field using property-ref
Asked Answered
O

1

18

I have a legacy database that I am mapping using NHibernate. The objects of concern are an Account and a list of Notification objects. The objects look like:

public class Notification
{
    public virtual int Id { get; set; }
    public virtual DateTime BatchDate { get; set; }
    /* other properties */

    public virtual Account Account { get; set; }
}

public class Account 
{
    public virtual int Id { get; set; }
    public virtual string AccountNumber { get; set; }
    /* other properties */ 
}

The mapping files look like:

<class name="Account" table="Account" dynamic-update="true">
<id name="Id" column="AccountID">
    <generator class="native" />
</id>
<property name="AccountNumber" length="15" not-null="true" />
    <!-- other properties -->
</class>

<class name="Notification" table="Notification">
    <id name="Id" column="Id">
        <generator class="native" />
    </id>
    <!-- other properties -->
    <many-to-one name="Account" class="Account" property-ref="AccountNumber" lazy="proxy">
        <column name="AcctNum" />
    </many-to-one>

However, when I create a criteria such as

return session.CreateCriteria(typeof(Notification)).List<Notification>();

I am getting a Select N+1 case where each account is loaded even though the Account is never referenced. Why are all of the accounts getting loaded when the many-to-one is mapped as a lazy proxy?

Oregon answered 2/3, 2009 at 22:39 Comment(1)
I get the same issue, I do remember seeing a post somehwere saying many-to-one property ref's can't be lazy loaded, I just cant find the source. This was with NH 1.2Zoophilous
E
15

The issue is caused by the property-ref attribute. Lazy loading only works when the many-to-one reference is using the other object's primary key since NHibernate assumes there's a foreign key constraint enforcing the validity of such a value. With a non-primary key (indicated by the property-ref), NHibernate does not make this assumption and thus does not assume the related object must exist. Since it does not want to create a proxy for an object that does not exist (i.e. should be null instead of a proxy), it eagerly fetches the remote object. This same issue exists when not-found="ignore" is specified since this indicates that the foreign key relationship is not enforced and may result in a null reference.

See also:

Elusion answered 30/9, 2010 at 16:11 Comment(1)
Issue in NH seems to be in TwoPhaseLoad.cs - it inspects LoadedState and calls IType.ResolveIdentifier to translate KEYs into Entities or Proxies. The IType involved here is ManyToOne(PK=false,Uniq=true,eager=false). It ends up calling ResolveIdentifier(value,session,owner) from base EntityType.cs, and that one checks IsReferenceToPrimaryKey(=false) and calls LoadByUniqueKey(). This one in turn has interesting comment "TODO: implement caching? proxies??" (YAY). Then, combo of session.PersistenceContext.GetEntity(new EntityUniqueKey(..key)) + persister.LoadByUniqueKey(..key) fetch the entity.Amphiboly

© 2022 - 2024 — McMap. All rights reserved.