-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 9 posts ] 
Author Message
 Post subject: One-To-One relationship always loads, even when marked Lazy?
PostPosted: Mon Jan 15, 2007 4:43 am 
Newbie

Joined: Mon Sep 25, 2006 11:02 pm
Posts: 10
Hi All,

The two relevant class mappings for this issue, Address and PropertyRecord, are below. When I load an Address using a Criteria query, the PropertyRecord is loaded with the Address even though it is marked Lazy. In the query I make no reference to the PropertyRecord, I simply create a like expression on Street Name.

This is my only One-To-One relationship and I am wondering if I have set up the mapping incorrectly?

Thanks,
KC

Hibernate version: 1.2.0 Beta (latest Build)

Mapping documents:

Code:
<!--Generated from NHibernate.Mapping.Attributes on 2007-01-15 02:23:36Z.-->
<hibernate-mapping default-access="field.camelcase-underscore" xmlns="urn:nhibernate-mapping-2.2">
  <class name="RealEstate.Data.Address, RealEstate.Data" lazy="true" table="`Address`">
    <id name="Id" column="AddressId" unsaved-value="0">
      <generator class="native" />
    </id>
    <property name="RBDI" column="RBDI" not-null="true" />
    <property name="Title" column="Title" not-null="true" />
    <property name="Street" column="Street" not-null="true" />
    <property name="City" column="City" not-null="true" />
    <property name="State" column="State" not-null="true" />
    <property name="Zip" column="Zip" not-null="true" />
    <property name="ZipPlus4" column="ZipPlus4" not-null="false" />
    <property name="WalkSequence" column="WalkSequence" not-null="true" />
    <property name="Endose" column="Endose" not-null="false" />
    <property name="CityOrRural" column="CityOrRural" not-null="false" />
    <property name="DPB" column="DPB" not-null="false" />
    <property name="DPBC" column="DPBC" not-null="false" />
    <property name="UpdateDate" column="UpdateDate" not-null="false" />
    <property name="Latitude" column="Latitude" not-null="false" />
    <property name="Longitude" column="Longitude" not-null="false" />
    <many-to-one name="CarrierRoute" class="RealEstate.Data.CarrierRoute, RealEstate.Data" column="CarrierRouteId" cascade="save-update" />
    <one-to-one name="PropertyRecord" class="RealEstate.Data.PropertyRecord, RealEstate.Data" cascade="all-delete-orphan" property-ref="Address" />
  </class>
  <class name="RealEstate.Data.PropertyRecord, RealEstate.Data" lazy="true" table="`PropertyRecord`">
    <id name="Id" column="PropertyRecordId" unsaved-value="0">
      <generator class="native" />
    </id>
    <property name="ParcelNumber" column="ParcelNumber" not-null="false" />
    <property name="District" column="District" not-null="false" />
    <property name="Situs" column="Situs" not-null="false" />
    <property name="LegalDescription" column="LegalDescription" not-null="false" />
    <property name="Owner" column="Owner" not-null="false" />
    <property name="OwnerAddress" column="OwnerAddress" not-null="false" />
    <property name="OwnerCity" column="OwnerCity" not-null="false" />
    <property name="OwnerState" column="OwnerState" not-null="false" />
    <property name="OwnerZip" column="OwnerZip" not-null="false" />
    <property name="IsHomestead" column="IsHomestead" not-null="false" />
    <property name="LotSize" column="LotSize" not-null="false" />
    <property name="YearBuilt" column="YearBuilt" not-null="false" />
    <property name="Quality" column="Quality" not-null="false" />
    <property name="NumberOfBedrooms" column="NumberOfBedrooms" not-null="false" />
    <property name="NumberOfBathrooms" column="NumberOfBathrooms" not-null="false" />
    <property name="FloorArea" column="FloorArea" not-null="false" />
    <property name="SlabArea" column="SlabArea" not-null="false" />
    <property name="CrawlSpace" column="CrawlSpace" not-null="false" />
    <property name="BasementArea" column="BasementArea" not-null="false" />
    <property name="GarageArea" column="GarageArea" not-null="false" />
    <many-to-one name="Address" class="RealEstate.Data.Address, RealEstate.Data" column="AddressId" unique="true" cascade="save-update" />
    <many-to-one name="PropertyType" class="RealEstate.Data.PropertyType, RealEstate.Data" column="PropertyTypeId" cascade="save-update" />
    <many-to-one name="Condition" class="RealEstate.Data.Condition, RealEstate.Data" column="ConditionId" cascade="save-update" />
    <many-to-one name="Roofing" class="RealEstate.Data.Roofing, RealEstate.Data" column="RoofingId" cascade="save-update" />
    <many-to-one name="Hvac" class="RealEstate.Data.Hvac, RealEstate.Data" column="HvacId" cascade="save-update" />
    <many-to-one name="ExteriorWall" class="RealEstate.Data.ExteriorWall, RealEstate.Data" column="ExteriorWallId" cascade="save-update" />
    <many-to-one name="Foundation" class="RealEstate.Data.Foundation, RealEstate.Data" column="FoundationId" cascade="save-update" />
    <many-to-one name="StructureType" class="RealEstate.Data.StructureType, RealEstate.Data" column="StructureTypeId" cascade="save-update" />
    <many-to-one name="StructureStyle" class="RealEstate.Data.StructureStyle, RealEstate.Data" column="StructureStyleId" cascade="save-update" />
    <many-to-one name="Garage" class="RealEstate.Data.Garage, RealEstate.Data" column="GarageId" cascade="save-update" />
    <set name="MiscImprovements" lazy="true" cascade="all-delete-orphan" inverse="true">
      <key column="PropertyRecordId" />
      <one-to-many class="RealEstate.Data.MiscImprovement, RealEstate.Data" />
    </set>
    <set name="PropertySales" lazy="true" cascade="all-delete-orphan" inverse="true">
      <key column="PropertyRecordId" />
      <one-to-many class="RealEstate.Data.PropertySale, RealEstate.Data" />
    </set>
  </class>


Name and version of the database you are using: MSSQL 2005

The generated SQL (show_sql=true):
Code:
SELECT this_.AddressId as AddressId3_1_, this_.RBDI as RBDI3_1_, this_.Title as Title3_1_, this_.Street as Street3_1_, this_.City as City3_1_, this_.State as State3_1_, this_.Zip as Zip3_1_, this_.ZipPlus4 as ZipPlus8_3_1_, this_.WalkSequence as WalkSequ9_3_1_, this_.Endose as Endose3_1_, this_.CityOrRural as CityOrR11_3_1_, this_.DPB as DPB3_1_, this_.DPBC as DPBC3_1_, this_.UpdateDate as UpdateDate3_1_, this_.Latitude as Latitude3_1_, this_.Longitude as Longitude3_1_, this_.CarrierRouteId as Carrier17_3_1_, propertyre2_.PropertyRecordId as Property1_9_0_, propertyre2_.ParcelNumber as ParcelNu2_9_0_, propertyre2_.District as District9_0_, propertyre2_.Situs as Situs9_0_, propertyre2_.LegalDescription as LegalDes5_9_0_, propertyre2_.Owner as Owner9_0_, propertyre2_.OwnerAddress as OwnerAdd7_9_0_, propertyre2_.OwnerCity as OwnerCity9_0_, propertyre2_.OwnerState as OwnerState9_0_, propertyre2_.OwnerZip as OwnerZip9_0_, propertyre2_.IsHomestead as IsHomes11_9_0_, propertyre2_.LotSize as LotSize9_0_, propertyre2_.YearBuilt as YearBuilt9_0_, propertyre2_.Quality as Quality9_0_, propertyre2_.NumberOfBedrooms as NumberO15_9_0_, propertyre2_.NumberOfBathrooms as NumberO16_9_0_, propertyre2_.FloorArea as FloorArea9_0_, propertyre2_.SlabArea as SlabArea9_0_, propertyre2_.CrawlSpace as CrawlSpace9_0_, propertyre2_.BasementArea as Basemen20_9_0_, propertyre2_.GarageArea as GarageArea9_0_, propertyre2_.AddressId as AddressId9_0_, propertyre2_.PropertyTypeId as Propert23_9_0_, propertyre2_.ConditionId as Conditi24_9_0_, propertyre2_.RoofingId as RoofingId9_0_, propertyre2_.HvacId as HvacId9_0_, propertyre2_.ExteriorWallId as Exterio27_9_0_, propertyre2_.FoundationId as Foundat28_9_0_, propertyre2_.StructureTypeId as Structu29_9_0_, propertyre2_.StructureStyleId as Structu30_9_0_, propertyre2_.GarageId as GarageId9_0_ FROM [Address] this_ left outer join [PropertyRecord] propertyre2_ on this_.AddressId=propertyre2_.AddressId WHERE ((this_.Street like @p0 and this_.City = @p1) and this_.State = @p2)
2007-01-15 02:24:09,725 [3604] INFO  NHibernate.Loader.Loader [(null)] <(null)> - SELECT propertyre0_.PropertyRecordId as Property1_9_0_, propertyre0_.ParcelNumber as ParcelNu2_9_0_, propertyre0_.District as District9_0_, propertyre0_.Situs as Situs9_0_, propertyre0_.LegalDescription as LegalDes5_9_0_, propertyre0_.Owner as Owner9_0_, propertyre0_.OwnerAddress as OwnerAdd7_9_0_, propertyre0_.OwnerCity as OwnerCity9_0_, propertyre0_.OwnerState as OwnerState9_0_, propertyre0_.OwnerZip as OwnerZip9_0_, propertyre0_.IsHomestead as IsHomes11_9_0_, propertyre0_.LotSize as LotSize9_0_, propertyre0_.YearBuilt as YearBuilt9_0_, propertyre0_.Quality as Quality9_0_, propertyre0_.NumberOfBedrooms as NumberO15_9_0_, propertyre0_.NumberOfBathrooms as NumberO16_9_0_, propertyre0_.FloorArea as FloorArea9_0_, propertyre0_.SlabArea as SlabArea9_0_, propertyre0_.CrawlSpace as CrawlSpace9_0_, propertyre0_.BasementArea as Basemen20_9_0_, propertyre0_.GarageArea as GarageArea9_0_, propertyre0_.AddressId as AddressId9_0_, propertyre0_.PropertyTypeId as Propert23_9_0_, propertyre0_.ConditionId as Conditi24_9_0_, propertyre0_.RoofingId as RoofingId9_0_, propertyre0_.HvacId as HvacId9_0_, propertyre0_.ExteriorWallId as Exterio27_9_0_, propertyre0_.FoundationId as Foundat28_9_0_, propertyre0_.StructureTypeId as Structu29_9_0_, propertyre0_.StructureStyleId as Structu30_9_0_, propertyre0_.GarageId as GarageId9_0_ FROM [PropertyRecord] propertyre0_ WHERE propertyre0_.AddressId=@p0
2007-01-15 02:24:09,756 [3604] INFO  NHibernate.Loader.Loader [(null)] <(null)> - SELECT carrierrou0_.CarrierRouteId as CarrierR1_17_0_, carrierrou0_.RouteCode as RouteCode17_0_ FROM [CarrierRoute] carrierrou0_ WHERE carrierrou0_.CarrierRouteId=@p0


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 15, 2007 1:19 pm 
Newbie

Joined: Mon Sep 25, 2006 11:02 pm
Posts: 10
After stepping through the code to determine what is causing this, I think I may have found the reason:

In EntityType.cs

Code:
public override object ResolveIdentifier(object id, ISessionImplementor session, object owner)
      {
         if (id == null)
         {
            return null;
         }
         else
         {
            if (IsReferenceToPrimaryKey)
            {
               return ResolveIdentifier(id, session);
            }
            else
            {
               return session.LoadByUniqueKey(AssociatedClass, uniqueKeyPropertyName, id);
            }
         }
      }


For my one to one association "IsReferenceToPrimaryKey" is false, and thus session.LoadByUniqueKey is called.

We see in SessionImpl.LoadByUnique call a TODO for adding Proxies & caching support. Could this be the culprit?

Code:
      public object LoadByUniqueKey(System.Type clazz, string uniqueKeyPropertyName, object id)
      {
         IUniqueKeyLoadable persister = (IUniqueKeyLoadable) Factory.GetEntityPersister(clazz);
         try
         {
            // TODO: Implement caching?! proxies?!
            object result = persister.LoadByUniqueKey(uniqueKeyPropertyName, id, this);
            return result == null ? null : ProxyFor(result);
         }
         catch (HibernateException)
         {
            // Do not call Convert on HibernateExceptions
            throw;
         }
         catch (Exception sqle)
         {
            throw Convert(sqle, "Error performing LoadByUniqueKey");
         }
      }


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 15, 2007 2:18 pm 
Newbie

Joined: Mon Sep 25, 2006 11:02 pm
Posts: 10
Hi All,

I would like to take a shot at making SessionImpl.LoadByUniqueKey create a proxy if needed.

Here are my thoughts, please let me know if I'm on the right track.

It seems that the issue here is that since we only have the UniqueKey which is not the primary key, we can not determine whether the object has already been loaded.

One solution would be to retrieve only the primary key if a proxy is needed. We could then use SessionImpl.InternalLoad to load the object since it will check the cache for the object or build a proxy if necessary. If no proxy is necessary we can still simply call persister.LoadByUniqueKey().

I guess the question is what does this really gain? If I have to query the db for the primary key why not just pull all of the data, which is what is occuring.

Any thoughts?


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 15, 2007 5:46 pm 
Expert
Expert

Joined: Tue Aug 23, 2005 5:52 am
Posts: 335
It looks like you've done a lot of research into this, but it's behavior by design I'm afraid. If you do a search of one-to-one and lazy loading on the Hibernate forum you'll find several posts by the developers explaining this.

In essence they say that if you need to check whether the object exists or not you might as well load it since the expensive part is normally finding the rows rather than returning the data.

Cheers,

Symon.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 15, 2007 7:53 pm 
Newbie

Joined: Mon Sep 25, 2006 11:02 pm
Posts: 10
Thanks Merge,

I understand why they choose not to lazy-load the one-to-one relationships. I'm still working on the extra select issue.

KC


Top
 Profile  
 
 Post subject: Re: One-To-One relationship always loads, even when marked Lazy?
PostPosted: Wed May 06, 2009 2:12 am 
Beginner
Beginner

Joined: Wed Aug 22, 2007 5:53 am
Posts: 38
Consider this scenatio :
I have one-to-one relation between Product and ProductBasic BUT at database level i have one-to-many between
PRODUCT and PRODUCTBASIC tables as history is mainted in the same table :

PRODUCT ----> PID (PK)
PRODUCTBASIC ---> PID (FK), USECODE , STARTTIMESTAMP forms Composite-key

PRODUCT
PID PNAME
1 Pen

PRODUCTBASIC
PID USECODE STARTTIME ENDTIME
1 100 06-MAY-2009 11:00 07-MAY-2009 10:00
1 100 07-MAY-2009 10:00 31-DEC-9999 00:00

Now while querying for ProductBasic, i will always fetch the row which satisfies the condition STARTTIME<CURRENTTIME<ENDTIME
and this will surely return me only one row.

Ideally the generated sql query should be :

select * from PRODUCT product left outer join PRODUCTBASIC productbasic on '100'=productbasic.USETYPE
and product.PID=productbasic.PID
and (
productbasic.STARTTIME<'09-MAY-2009 10:00'
and '09-MAY-2009 10:00'<productbasic.ENDTIME
)
where
product.PID='1'

PLEASE NOTE THAT i am not using the composite-key to establish the relationship. Instead i am using

1) product.PID=productbasic.PID
2) productbasic.USETYPE='100'
3) productbasic.STARTTIME<'09-MAY-2009 10:00' and '09-MAY-2009 10:00'<productbasic.ENDTIME

This last condition can only be provided at runtime as we will get the currenTime value at runtime only.
Now, if we allow productBasic to be fetched non-lazyly compulsary, it will form the query using only 1) & 2) conditions
and it will not take condition 3) as it is not provide in .hbm.xml file..
The incorrect query thus formed will be
select * from PRODUCT product left outer join PRODUCTBASIC productbasic on '100'=productbasic.USETYPE
and product.PID=productbasic.PID where product.PID='1'

This incorrect query will fetch more than one rows and throw an error:
org.hibernate.HibernateException: More than one row with the given identifier was found: for class ProductBasic.


So now 2 things are must needed
1) one-to-one with property-ref should be allowed to be loaded lazily.
2) one-to-one should allow the use of filters (so that runtime conditions can be specified, 3) condition in my case)


Top
 Profile  
 
 Post subject: Re: One-To-One relationship always loads, even when marked Lazy?
PostPosted: Tue May 26, 2009 8:46 am 
Beginner
Beginner

Joined: Wed Aug 22, 2007 5:53 am
Posts: 38
I have this requirement because i use filter on ProductBasic. and now when this ProductBasic is fetched always (non-lazyly) it doesn't
apply my filter.


Top
 Profile  
 
 Post subject: Re: One-To-One relationship always loads, even when marked Lazy?
PostPosted: Sat Jul 18, 2009 5:00 pm 
Regular
Regular

Joined: Tue Feb 19, 2008 6:05 pm
Posts: 82
Forget one-to-one as it is designed not to fetch non-lazily, or atleast without the proxy or runtime-generated subclassing as per https://www.hibernate.org/315.html.

I have been using many-to-one happily, only to find that even the many-to-one parent objects are being eagerly loaded, as per viewtopic.php?f=1&t=991369

Quote:
One-to-one will not work the way we want it as it just cannot be used to fetch lazily for 1<---->0 or 1 relationship, https://www.hibernate.org/315.html and https://www.hibernate.org/162.html

But why is the many-to-one being eagerly loaded, when property-ref is used?

A many-to-one maps with lazy="proxy" by default, meaning it does not eagerly fetch the parent object, however, I noticed that if I use the property-ref to map to an other property field, they it is by default doing an eager fetch, no matter what other property options I set.

When will I find an answer to this problem?

Regards
Krishna


Any ideas around,
Krishna


Top
 Profile  
 
 Post subject: Re: One-To-One relationship always loads, even when marked Lazy?
PostPosted: Fri Jan 29, 2010 10:05 am 
Beginner
Beginner

Joined: Fri Jan 23, 2009 10:34 am
Posts: 25
Location: Switzerland
See http://opensource.atlassian.com/project ... e/HHH-2753


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 9 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.