-->
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: Parent-Child, casecade, and efficient bidirectional assoc
PostPosted: Mon Jul 31, 2006 4:23 pm 
Newbie

Joined: Tue Jan 10, 2006 9:42 pm
Posts: 5
Need help with Hibernate? Read this first:
http://www.hibernate.org/ForumMailingli ... AskForHelp

Hibernate version:3.1

I'm confused about the correct way to map a parent-child relationship efficiently. The common problems faq, http://hibernate.org/116.html#A10, indicates that you must update both sides of a bi-directional association, as follows:

Code:
parent.getChildren().add(child);
child.setParent(parent);


However, to be efficient, the tips and tricks FAQ, http://hibernate.org/118.html#A20, suggests that you can create an association to an object without actually loading the object from the database (assuming lazy enabled):
Code:
Item itemProxy = (Item) session.load(Item.class, itemId);
Bid bid = new Bid(user, amount, itemProxy);
session.save(bid);


Assuming for a moment that Item is parent and Bids is child, and Items had a collection of Bids, it would be necessary to add bid to that collection (e.g.: itemProxy.getBids().add(bid)). The act of calling a method on itemProxy will load it (or so it seems by experiment). So, how you do you work around this?

The performance FAQ, http://hibernate.org/117.html#A9 suggests that you can refactor to use many-to-one associations to avoid this problem. However, that means that the Parent has no association to the Child, and so you cannot define a cascade saves, updates and deletes from parent to child, which is often desirable (e.g. sometimes, you'll have the Parent and want to cascade an update, sometimes you'll create a new association, and don't want to reload the parent, if you can help it.

So is it possible to have a parent-child relationship with cascade (parent to child), without requiring that the Parent be loaded just to add a child (assuming you know the ID of the parent)? If this were simple SQL, I'd just do an insert, and as long as the parent exists, it works. Of course, in SQL, I've also got to map all the attributes and explicitly insert (or update) myself.

thanks!


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jul 31, 2006 6:38 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
No, it's not possible, unless you do it directly via JDBC. To add something to a proxied entity (the parent), the entity has to be deproxied. However, the rule that you must update both sides of a bidirectional association applies only when you intend to use the in-memory objects afterwards: if you don't, then you only have to update the side that contains the reference. So for a many-to-one, you only have to update the many side (as in the DB, it contains the ID of the one side).

The normal bidirectional many-to-many will insert into the join table when saving either the parent or child. Assuming that the parent is currently proxied, then this code will work without deproxying the parent:
Code:
child.setParent(parent);
session.save(child);
session.flush();
If, at some later point in your code, you access parent.getChildren(), it will include the child object in this code. If, however, parent had already been deproxied at this point, then parent.getChildren() will not include the new child.

_________________
Code tags are your friend. Know them and use them.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jul 31, 2006 7:53 pm 
Newbie

Joined: Tue Jan 10, 2006 9:42 pm
Posts: 5
tenwit wrote:
No, it's not possible, unless you do it directly via JDBC. To add something to a proxied entity (the parent), the entity has to be deproxied. However, the rule that you must update both sides of a bidirectional association applies only when you intend to use the in-memory objects afterwards: if you don't, then you only have to update the side that contains the reference. So for a many-to-one, you only have to update the many side (as in the DB, it contains the ID of the one side).

The normal bidirectional many-to-many will insert into the join table when saving either the parent or child. Assuming that the parent is currently proxied, then this code will work without deproxying the parent:
Code:
child.setParent(parent);
session.save(child);
session.flush();
If, at some later point in your code, you access parent.getChildren(), it will include the child object in this code. If, however, parent had already been deproxied at this point, then parent.getChildren() will not include the new child.


OK, pretty much what I thought. If fact, I had already tried updating the one side, with the results you described. If I understand correctly, I could evict the parent (after the save) to ensure that noone gets the invalid object. However, this starts to feel kludgey pretty fast. I may be forcing a flush, and if the object is already in the session cache, I'm evicting it when I would have been better off just adding the child. Is it possible to determine if an object is a proxy and act accordingly: update both sides if deproxied, just the one if not? (Not sure I want to pursue that route, just curious).

So basically, it sounds like you have to choose between the convenience (and transparency) provided by cascade and the more efficient, but less transparent (i.e. you have to code explicit saves/updates) many-to-one association.


An observation: it seems to me this might be a feature request. Lazy properties are available, what I want is the reverse. Given an object, if already deproxied, update the association, if not (and the association is a bag) do not load the object. Since it is "inverse", it does not get persisted, anyway. (or something like that).


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jul 31, 2006 8:06 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
Yes, you can determine if an object is proxied. Use this code:
Code:
boolean isProxy = (!session.contains(object) || !Hibernate.isInitialized(object));
If you want to be explicit about the state of an entity, you could use this:
Code:
// Define an enum or static final int called HibernateState for DETACHED, PROXIED and PERSISTENT.
public HibernateState
getHibernateState(Session s, Object object)
{
  // Note that if you have a common interface for all persistable
  // objects, with a single getId() method or similar, you can also
  // check for TRANSIENT state by checking getId() == unsaved-value.
  if (!s.contains(object))
  {
    return DETACHED;
  }
  else if (Hibernate.isInitialized(object))
  {
    return PROXIED;
  }
  return PERSISTENT;
}

_________________
Code tags are your friend. Know them and use them.


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.