-->
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.  [ 11 posts ] 
Author Message
 Post subject: Set of child entities without primary keys
PostPosted: Tue Jan 08, 2008 1:33 pm 
Regular
Regular

Joined: Wed Aug 15, 2007 7:37 am
Posts: 73
Hi,

I've got a set of child entities that don't have any primary key, just a foreign key to a parent (e.g. there's no way of determining an individual child, they can only be treated as a collection). I only ever seem to get one child entity in the ISet. If I add a primary key (e.g. change the id tag to a surrogate id) I get the expected result. The SQL being executed looks correct, but I'm assuming it's something to do with Child equality? Any help appreciated.

Code below has been edited for brevity.

Code:

public class Parent
{
  public long Id
  {
     get { .. }     set { .. }
  }
  public ISet<Child> Children
  {
     get { .. }     set { .. }
  }
}

public class Child
{
  public Parent Parent
  {
     get { .. }      set { .. }
  }
 
  public long ParentId
  {
     get { .. }      set { .. }
  }
}

and mapping files:
Code:
<class name="Parent" lazy="false" table="parent">
  <id name="Id">
      <column name="id" not-null="true" sql-type="int(14)" />
      <generator class="assigned" />
  </id>

  <set name="Children" table="children" cascade="all-delete-orphan" inverse="true" lazy="true">
      <key column="parent_id"/>
      <one-to-many class="Child"/>
    </set>
</class>

<class name="Child" lazy="false" table="children">
  <id name="ParentId">
      <column name="parent_id" not-null="true" sql-type="int(14)"/>
      <generator class="foreign">
        <param name="property">Parent</param>
      </generator>
    </id>

    <many-to-one name="Parent" class="Parent" not-null="true" column ="parent_id"/>
</class>


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jan 08, 2008 6:55 pm 
Regular
Regular

Joined: Wed Jan 25, 2006 1:11 am
Posts: 118
Location: Copenhagen, Denmark
The identifier for your child objects are the parents identifer, two objects related to the same parent will have the same identifier and therefore be identical when compared by nHibernate.

So adding two transient children to your parent(and save them) will fail because an object with that identifier is already in the session.

I must admit i fail to see what you are trying to accomplish.

A second thing is that you are using a "set" and from what i see you want to allow duplicates.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jan 08, 2008 7:12 pm 
Regular
Regular

Joined: Wed Aug 15, 2007 7:37 am
Posts: 73
Thanks for the response, your explanation makes sense.

I'm trying to model an unordered set of children that refer to a parent object but don't in themselves have any identifier - an id column here makes no sense, as individually the children are of no interest. Is overriding Equals enough here? The objects should only be considered equal if all the fields are equal (the fields were omitted in the example), not if ParentId is equal.

The behaviour I want is that if changes are made to the collection the whole thing is deleted and re-saved (i.e. DELETE FROM children WHERE parent_id = ? ; INSERT ... ). Maybe this isn't something hibernate does; our model requires this in a number of places for good or for ill.

Thanks,

Steve


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jan 08, 2008 7:34 pm 
Regular
Regular

Joined: Wed Jan 25, 2006 1:11 am
Posts: 118
Location: Copenhagen, Denmark
Quote:
The objects should only be considered equal if all the fields are equal (the fields were omitted in the example), not if ParentId is equal


This is what i would call a value-type, in nHibernate this is done using components as described in the reference manual chapter 7

You can easily make it work like you describe by mapping your parent class like this(the Child class is now a "component" instead of a class this way it has no life of its own in nHibernates eyes)

Code:
<class name="Parent" lazy="false" table="parent">
    <id name="Id">
      <column name="id" not-null="true" />
      <generator class="assigned" />
    </id>
    <set name="Children" table="children">
      <key column="parent_id"/>
      <composite-element class="Child">
        <property name="Name" />
      </composite-element>
    </set>
  </class>


I added name as an example for your components properties, it can of course have multiple properties.

Hope this helps


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jan 09, 2008 12:09 pm 
Regular
Regular

Joined: Wed Aug 15, 2007 7:37 am
Posts: 73
Hi,

That appears to work for loading, thanks for the help. NHibernate doesn't seem to be saving the collection now though... the mapping file is pretty much as you suggested. Any ideas?

Code:
<set name="Children" table="children" cascade="all" inverse="true" lazy="true">
  <key column="parent_id"/>
      <composite-element class="Child">
        <property name="Name"/>
  </composite-element>
</set>


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jan 09, 2008 12:19 pm 
Regular
Regular

Joined: Wed Jan 25, 2006 1:11 am
Posts: 118
Location: Copenhagen, Denmark
How does the code look where you add/remove from the collection?


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jan 09, 2008 12:27 pm 
Regular
Regular

Joined: Wed Aug 15, 2007 7:37 am
Posts: 73
Sorry, it looks like this..

Code:
Child c = new Child(Parent, "name");
parent.Children.Add(c);
session.Save(parent);


Even if I change an existing child I don't get any UPDATES in the SQL Hibernate's producing - it's like it's decided not to track changes at all. As an aside, I haven't overriden Equals and GetHashCode (although doing so didn't appear to have any effect on this). Again, thanks for your help on this.

Steve


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jan 09, 2008 12:38 pm 
Regular
Regular

Joined: Wed Jan 25, 2006 1:11 am
Posts: 118
Location: Copenhagen, Denmark
Remove inverse="true" from your <set> tag in the mapping


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jan 09, 2008 12:51 pm 
Regular
Regular

Joined: Wed Aug 15, 2007 7:37 am
Posts: 73
That was stupid of me - thanks again for the help. Just out of interest, I notice in the SQL that NHibernate runs separate DELETE and INSERTs for each element of the collection - is there a way to amalgamate these operations (since the entire collection's keyed off the parent id)?

Thanks again for your help, appreciate it.

Steve


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jan 09, 2008 1:00 pm 
Regular
Regular

Joined: Wed Jan 25, 2006 1:11 am
Posts: 118
Location: Copenhagen, Denmark
The only reason i can think of for deleting the children one by one is that you don't wan't to delete children that could have been added since you retrieved the collection


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jan 09, 2008 7:04 pm 
Regular
Regular

Joined: Wed Aug 15, 2007 7:37 am
Posts: 73
Yes, true. Although I would've thought that the responsibility for that would be down to using locking (as it would be with entity updates where the data could've changed underneath you). It doesn't matter for small collections but for larger ones it gets inefficient.


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