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.  [ 1 post ] 
Author Message
 Post subject: Summary + Detail pattern (lazy one-to-one) question
PostPosted: Sat Nov 22, 2008 7:58 pm 
Newbie

Joined: Thu Aug 10, 2006 9:53 am
Posts: 2
I apologies if this has already been answered else where but although I've come across a lot of a material describing why the one-to-one mapping does not support lazy loading I've not found any concrete examples of working around this for what must be a very common pattern.

For performance reasons I want to split a number of my classes into the main class (lets call it the summary class) and a detail class that will be available through a property of the main or summary class. The idea being that many of these classes will simply be loaded for display in a grid where some of the (large) detail properties are not required, there will be situations however where the details for an individual item will be required and then I would like to go out to the database to get these individually (lazy load them).

So I set everything up with the one-to-one mapping as shown below (commented out) however as that does not perform a lazy loading of the detail class that is not what I want. If I try replacing the one-to-one with a many-to-one as I've read about I get an exception when trying to save a new item, exception is shown below.

Surely this is a very common pattern, how should I implement this?

Hibernate version:

2.0.1 GA

Mapping documents:

Code:
<?xml version="1.0"?>

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
   <class name="Test.Asset, Test" table="Assets">
      <id name="Id" column="Id" type="Int32" unsaved-value="-1" access="nosetter.camelcase">
         <generator class="identity"/>
      </id>

      <property name="Name" column="Name" type="String" length="64"/>
      
      <!-- <one-to-one name="Detail" class="Test.AssetDetail, Test" cascade="all"/> -->
      
      <many-to-one name="Detail" column="Id" class="Test.AssetDetail, Test" cascade="all"/>
   </class>
   
   <class name="Test.AssetDetail, Test" table="AssetDetails">
      <id name="AssetId" column="AssetId" type="Int32" unsaved-value="-1" access="nosetter.camelcase">
         <generator class="foreign">
            <param name="property">Asset</param>
         </generator>
      </id>
      
      <one-to-one name="Asset" class="Test.Asset, Test" constrained="true"/>
      
      <property name="ABigProperty" column="ABigProperty" type="String" length="64"/>
   </class>
</hibernate-mapping>


Code:

Code:
using System;

using NHibernate;
using NHibernate.Cfg;

namespace Test
{
   public class Asset
   {
      private int id;
      private string name;
      private AssetDetail detail;
      
      public Asset()
      {
         id = -1;
         
         detail = new AssetDetail();
         
         detail.Asset = this;
      }
      
      public virtual int Id
      {
         get {
            return(id);
         }
      }
      
      public virtual string Name
      {
         get {
            return(name);
         }
         
         set {
            name = value;
         }
      }
      
      public virtual AssetDetail Detail
      {
         get {
            return(detail);
         }
         
         set {
            detail = value;
         }
      }
   }
   
   public class AssetDetail
   {
      private int assetId;
      private Asset asset;
      private string aBigProperty;
      
      public AssetDetail()
      {
         assetId = -1;
      }
      
      public virtual int AssetId
      {
         get {
            return(assetId);
         }
      }
      
      public virtual Asset Asset
      {
         get {
            return(asset);
         }
         
         set {
            asset = value;
         }
      }
      
      public virtual string ABigProperty
      {
         get {
            return(aBigProperty);
         }
         
         set {
            aBigProperty = value;
         }
      }
   }
   
    public class Test
   {
      public static void Main(string[] arguments)
      {
         Configuration configuration;
         ISessionFactory sessionFactory;
         ISession session;
         ITransaction transaction;
         Asset asset;
         
         configuration = new Configuration();
         
         configuration.Configure();
         
         sessionFactory = configuration.BuildSessionFactory();
         
         session = sessionFactory.OpenSession();

         transaction = session.BeginTransaction();

         asset = new Asset();
         
         asset.Name = "Test";
         asset.Detail.ABigProperty = "Blah blah blah";

         session.Save(asset);

         transaction.Commit();

         Console.WriteLine(asset.Id.ToString());
         
         session.Close();

         sessionFactory.Close();
      }
    }
}


Full stack trace of any exception that occurs:

Code:
Unhandled Exception: NHibernate.Id.IdentifierGenerationException: null id generated for:Test.AssetDetail
   at NHibernate.Event.Default.AbstractSaveEventListener.SaveWithGeneratedId(Object entity, String entityName, Object anything, IEventSource source, Boolean req
uiresImmediateIdAccess)
   at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.SaveWithGeneratedOrRequestedId(SaveOrUpdateEvent event)
   at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.EntityIsTransient(SaveOrUpdateEvent event)
   at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.PerformSaveOrUpdate(SaveOrUpdateEvent event)
   at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.OnSaveOrUpdate(SaveOrUpdateEvent event)
   at NHibernate.Impl.SessionImpl.FireSaveOrUpdate(SaveOrUpdateEvent event)
   at NHibernate.Impl.SessionImpl.SaveOrUpdate(String entityName, Object obj)
   at NHibernate.Engine.CascadingAction.SaveUpdateCascadingAction.Cascade(IEventSource session, Object child, String entityName, Object anything, Boolean isCasc
adeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeToOne(Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeAssociation(Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeProperty(Object child, IType type, CascadeStyle style, Object anything, Boolean isCascadeDeleteEnabled)
   at NHibernate.Engine.Cascade.CascadeOn(IEntityPersister persister, Object parent, Object anything)
   at NHibernate.Event.Default.AbstractSaveEventListener.CascadeBeforeSave(IEventSource source, IEntityPersister persister, Object entity, Object anything)
   at NHibernate.Event.Default.AbstractSaveEventListener.PerformSaveOrReplicate(Object entity, EntityKey key, IEntityPersister persister, Boolean useIdentityCol
umn, Object anything, IEventSource source, Boolean requiresImmediateIdAccess)
   at NHibernate.Event.Default.AbstractSaveEventListener.PerformSave(Object entity, Object id, IEntityPersister persister, Boolean useIdentityColumn, Object any
thing, IEventSource source, Boolean requiresImmediateIdAccess)
   at NHibernate.Event.Default.AbstractSaveEventListener.SaveWithGeneratedId(Object entity, String entityName, Object anything, IEventSource source, Boolean req
uiresImmediateIdAccess)
   at NHibernate.Event.Default.DefaultSaveEventListener.SaveWithGeneratedOrRequestedId(SaveOrUpdateEvent event)
   at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.EntityIsTransient(SaveOrUpdateEvent event)
   at NHibernate.Event.Default.DefaultSaveEventListener.PerformSaveOrUpdate(SaveOrUpdateEvent event)
   at NHibernate.Event.Default.DefaultSaveOrUpdateEventListener.OnSaveOrUpdate(SaveOrUpdateEvent event)
   at NHibernate.Impl.SessionImpl.FireSave(SaveOrUpdateEvent event)
   at NHibernate.Impl.SessionImpl.Save(Object obj)
   at Test.Test.Main(String[] arguments) in Test.cs:line 119


Name and version of the database you are using:

Microsoft SQL Server 2005

Code:
create table dbo.Assets (
   Id int identity(1, 1) not null,
   Name nvarchar(64) not null,

   constraint PK_Assets primary key (Id) )
go

create table dbo.AssetDetails (
   AssetId int not null,
   ABigProperty nvarchar(64) not null,

   constraint PK_AssetDetails primary key (AssetId),

   constraint FK_AssetDetails_Assets foreign key (AssetId) references dbo.Assets (Id) )
go


The generated SQL (show_sql=true):

None

Debug level Hibernate log excerpt:


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

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.