-->
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.  [ 4 posts ] 
Author Message
 Post subject: Auto-wiring bidirectional associations
PostPosted: Sat Nov 05, 2005 1:32 pm 
Newbie

Joined: Thu Oct 27, 2005 9:23 am
Posts: 5
Hello,

given a bidirectional association between to classes, say Parent and Child. You have to maintain both sides of the association:

Code:
Parent parent = new Parent();
Child child = new Child();

child.Parent = parent;
parent.AddChild(child);



Because this is error prone I tried to solved the problem by implementing the classes as follows:

Code:
class Parent {
   void AddChild(Child child) {
      if (!m_Children.Contains(child)) {
         m_Children.Add(child);
         child.Parent = this;
      }
   }
}


class Child {
   Parent Parent {
      get { return m_Parent; }
      set {
         if (m_Parent != null) {
            m_Parent = value;
            value.AddChild(this);
         }
      }
   }
}



To be able to load both classes with NHibernate I added the following collection to class Parent:

Code:
   IList Children {
      get { return m_Children; }
      set { m_Children = value; }
   }



But now I have a problem with lazy loading of the Children collection of class Parent. If the collection is lazy loaded and after that one of the Child objects is loaded, a call to the setter of Child.Parent tries to add the child to it's parents children collection, which is not loaded. A LazyInitializationException is raised.

So my question is: how do you solve this problem? Is it indeed necessary to handle both sides of bidirectional associations "by hand" if using NHibernate?


Sincerely,
Stefan Lieser


Top
 Profile  
 
 Post subject:
PostPosted: Sun Nov 06, 2005 11:00 am 
Contributor
Contributor

Joined: Thu May 12, 2005 9:45 am
Posts: 593
Location: nhibernate.org
If you set Inverse="true" in your collection, the <many-to-one> side will be responsible of the relationship. (read the doc for more details...)

So you will just have to do:
Code:
child.Parent = parent;


And if you only do:
Code:
parent.Add(child);

And when saving the parent, the child will not be associated to this parent (even if cascade is enabled)...

_________________
Pierre Henri Kuaté.
Get NHibernate in Action Now!


Top
 Profile  
 
 Post subject:
PostPosted: Sun Nov 06, 2005 11:31 am 
Well, I have Inverse="true" set already, that's not the problem.

As long as my domain objects are handled by NHibernate both sides of the bidirectional association are handled by NH. But many of my unit tests run just on the domain objects, and there I have to set both sides.

Btw. if I set only Parent.Add(child), NHibernate sets up the database association correct. But it does not set the other side of the association in memory. The other side is setup only if I load both objects back from database. Am I right?

Up to now I'm thinking of the following solution to the problem:
- The association should only be set on the parent object. For example Parent.AddChild(child)
- The parent adds the child to it's internal collection and sets Child.Parent = this so that the other side is set up automatically.

This does not conflict with NHibernate. Any suggestions?


Sincerely,
Stefan Lieser


Top
  
 
 Post subject:
PostPosted: Mon Nov 07, 2005 6:16 pm 
Expert
Expert

Joined: Fri Oct 28, 2005 5:38 pm
Posts: 390
Location: Cedarburg, WI
One of the reasons we'd like to have strongly typed collections is so the collection itself can do this. E.g. the parent's code would look like this:

Code:
public class Parent
{
    private ChildCollection _children;

    public ChildCollection Children
    {
        get {return this._children;}
    }

    public Parent()
    {
        this._children = new ChildCollection(this);
    }
}


The ChildCollection would be attached to a specific parent, only accept children of a specific type, and would automatically attach the child to the parent (the child type must implement IChild to support this):

Code:
public class ChildCollection : IList
{
    private Parent _parent;
    private ArrayList _innerList;

    public ChildCollection(object parent, Type childType)
    {
        this._parent = parent;
        this._innerList = new ArrayList();
    }

    public int Add(object item)
    {
        this.ValidateType(item);
        if (item.Parent != null)
        {
            // should we just remove the item from the old parent,
            // or should we throw an exception?
        }

        this._innerList.Add(item);
        item.Parent = this._parent;
    }

    private void ValidateType(object item)
    {
        if (item == null) throw ...
        if (!item is Child) throw ...
   }
}


To support this, we'd need additional tags for <one-to-many> in the mapping files, e.g.

<bag ...>
<one-to-many
class="Child"
collection-class="ChildCollection"
collection-constructor-takes-owner="true"
/>
</bag>


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