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.  [ 6 posts ] 
Author Message
 Post subject: I must have done something daft...
PostPosted: Tue Feb 05, 2008 11:59 am 
Beginner
Beginner

Joined: Sun Nov 18, 2007 10:39 am
Posts: 46
Location: Liverpool, England
i've been playing with the NHibernate sources to support a mapping we need, and whilst testing it I found that adding children to a parent collection wasn't working. I scaled back to the simple case that should have worked without any mods, but that exhibited the same problem. After getting nowhere looking for the error, I plugged my home built library into an application that worked with the downloaded library, expecting to get the same problem, but it worked. I'm now confused, and suspect I've done something daft with my code or mapping, but I can't see what. Can anyone point me at the doubtless glaringly obvious mistake?

Here are the test tables, pretty simple:
Code:
Table Parent
  ID int not null Identity, primary key
  Name varchar(50) not null

Table Child
  ID int not null Identity, primary key
  Name varchar(50) not null
  ParentID int not null, foreign key linked to ID in Parent


Here is the mapping:
Code:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="DBKeyTest" assembly="DBKeyTest">
  <class name="DBKeyTest.IDParent, DBKeyTest" table="IDParent">
    <id name="ID" column="ID" unsaved-value="0">
      <generator class="native"/>
    </id>
    <property name="Name" type="String" />
    <bag name="ChildList" inverse="true" lazy="true" cascade="all-delete-orphan">
      <key column="ParentID"/>
      <one-to-many class="DBKeyTest.IDChild, DBKeyTest"/>
    </set>
  </class>
  <class name="DBKeyTest.IDChild, DBKeyTest" table="IDChild">
    <id name="ID" column="ID" unsaved-value="0">
      <generator class="native"/>
    </id>
    <property name="Name" type="String" />
    <many-to-one name="Parent" column="ParentID" class="DBKeyTest.IDParent, DBKeyTest" not-null="true"/>
  </class>
</hibernate-mapping>


and here's the sample code that's giving the error:
Code:
Private Sub SimpleChildTest(ByVal session As ISession)
   Dim parent As IDParent
   parent = session.Load(GetType(IDParent), 18)
   parent.ChildList = New List(Of IDChild)
   Dim child As New IDChild
   child.Name = "Bart"
   child.Parent = parent
   parent.ChildList.Add(child)
   child = New IDChild
   child.Name = "Lisa"
   child.Parent = parent
   parent.ChildList.Add(child)
   session.Update(parent)
   session.Flush()
End Sub

[From IDParent]
Private mChildList As List(Of IDChild)
Public Overridable Property ChildList() As List(Of IDChild)
   Get
      Return mChildList
   End Get
   Set(ByVal Value As List(Of IDChild))
      mChildList = Value
   End Set
End Property

[From IDChild]
Private mParent As IDParent
Public Overridable Property Parent() As IDParent
   Get
      Return mParent
   End Get
   Set(ByVal Value As IDParent)
      mParent = Value
   End Set
End Property


Apart from the properties given, IDParent and IDChild just consist of simple constructors and simple scalar properties for the remain properties in the mappings.

When I run the code, I get the error
Quote:
You may not dereference an collection with cascade="all-delete-orphan"


If I remove the child.Parent=parent lines, I get the following error instead:
Quote:
not-null property references a null or transient value: DBKeyTest.IDChild.Parent


Presumably I've got a mistake somewhere, but so far examing it or comparing it to similar code in the working application hasn't shed any light.

Incidentally, looking at the examples from the documentation, the latter should work, but in my (working) apps I always have to set the parent reference explicitly - which way should it work?

Thanks,
Kev


Top
 Profile  
 
 Post subject:
PostPosted: Tue Feb 05, 2008 3:29 pm 
Expert
Expert

Joined: Fri Oct 28, 2005 5:38 pm
Posts: 390
Location: Cedarburg, WI
I think we explicitly call session.Save(child) after each child is added to a parent's collection, even though it shouldn't be necessary. I seem to remember NHibernate having some bug where doing this helped ...


Top
 Profile  
 
 Post subject:
PostPosted: Tue Feb 05, 2008 6:38 pm 
Beginner
Beginner

Joined: Sun Nov 18, 2007 10:39 am
Posts: 46
Location: Liverpool, England
Hi Nels,
I'll give it a try when I get in tomorrow, but I'm not sure it's related. The working application doesn't do this, but works okay when adding multiple children to a collection

Kev


Top
 Profile  
 
 Post subject:
PostPosted: Tue Feb 05, 2008 7:06 pm 
Beginner
Beginner

Joined: Sun Nov 18, 2007 10:39 am
Posts: 46
Location: Liverpool, England
Aha, spotted it - I knew it was something daft. In my working example the classes are more intelligent, and amongst other things automatically set the collection to a new instance if the property get is accessed and the list is not yet set. In my test code I was always setting the list even after a load, and doing so for a loaded object causes the dereference error

Still doesn't work if I just add the child to the parent's collection without setting the reference in the child as well - are the docs just wrong when they say you can do this?

Kev


Top
 Profile  
 
 Post subject:
PostPosted: Tue Feb 05, 2008 11:30 pm 
Expert
Expert

Joined: Fri Oct 28, 2005 5:38 pm
Posts: 390
Location: Cedarburg, WI
Yeah, you're right about having to set the collection instance manually if it's null, we have to do the same.

From what I remember, you do have to set the reference to the parent on the child as well as adding the child to the parent's collection. We solved the problem by making our own collection class that knows who the parent ("owner") is and what the collection item type's "owner" property name is. Then when you add (or remove) items to an "owned" collection, it automatically sets (or clears) the "owner" on the item. I don't know why more people don't do this, since we have quite a number of entities with a dozen or more collection properties, and it clutters up the entity's API to have to define methods like AddFoo(), AddBar(), etc. for every collection property (not to mention it stinks of poor OO design).


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 06, 2008 5:32 am 
Beginner
Beginner

Joined: Sun Nov 18, 2007 10:39 am
Posts: 46
Location: Liverpool, England
For child entities that must belong to a parent I tend to make the default constructor private and provide a public constructor that takes a reference to the parent. Your way seems neater though


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 6 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.