-->
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.  [ 5 posts ] 
Author Message
 Post subject: Mapping a subclass as a component
PostPosted: Fri Sep 14, 2007 2:24 pm 
Newbie

Joined: Fri Sep 14, 2007 2:02 pm
Posts: 10
Location: New York, NY
I've got a fairly fundamental object mapping question, that I'm hoping to get help with.

The portion of my object model I'm concerned with here has to do with an Entity called Bond. A Bond has some coupons, which can be one of two types: FixedMaturing or FloatingMaturing.

In conceptual terms, the coupons object is a component of Bond. Ideally, I'd like to have that concept carried over in to the Hibernate mapping... for example, I wouldn't want to call save() on the Coupons object, I should just call save on the containing Bond. And a Coupons object shouldn't need an ID or a version, cause it's really just a field of the Bond.

My understanding though is that if you want to use polymorphic objects, you must use Entities. So we've got a Coupons base class, which in turn has two subclasses.

This works ok, but there's one thing about it that bugs me, which is that if I modify the Coupons object by changing one of it's fields, Hibernate doesn't see that the Bond object is dirty and should have it's version updated, although the Coupons object does.

Is there a better way to model polymorphic 'component' type objects?

I've come up with a couple of ideas, but I'm not sure how well they'd work:

Make a component object that can store all the varied fields of the Coupons subclasses, and take care of instantiating the correct subtype of Coupons myself. That seems a bit awkward though, as I'd basically be reimplementing what Hibernate does with it's entity subclass handling.

Implement an Interceptor that, onFlushDirty of a Coupons object, does something to the containing Bond to mark it as dirty.

Any advice?

Hibernate version:
2.1.7c, but upgrading to up-to-date version shortly.


Top
 Profile  
 
 Post subject:
PostPosted: Sat Sep 15, 2007 7:27 pm 
Beginner
Beginner

Joined: Thu Jun 17, 2004 11:25 pm
Posts: 21
Location: Los Angeles
By definition, a component stores all its properties in the parent's table, so there's no getting around having a column for each property that is in any of the Coupon classes. In other words, one way or another you're going to end up with "a component object that can store all the varied fields of the Coupons subclasses" because each field maps to a different column in the table. You'll also need a discriminator column to tell the different types apart. As you point out, what you've done by this point is re-invented Hibernate's "table per class hierarchy" system just to avoid making Coupons entities.

I think the best solution is to make Coupons entities using either "table per class hierarchy" or "table per subclass". Tie the Coupons' life cycle to the Bond's by using cascade="all, delete-orphan" on the Bond's collection of Coupons.

=Jeremy=


Top
 Profile  
 
 Post subject:
PostPosted: Mon Sep 17, 2007 10:49 pm 
Newbie

Joined: Fri Sep 14, 2007 2:02 pm
Posts: 10
Location: New York, NY
Yep, that jibes with what I know as well.

What it doesn't get me though is the ability to detect changes to a Bond through it's version, if the only actual changes are to the referenced Coupons object. My understanding is that on saving such a Bond, the Coupons will be saved via the cascade because it's state is dirty. However, the isDirty check on the Bond itself will see that it's still referring to the same actual entity, and be marked as false.

It would be handy if there was a straightforward way to have the Bond version get incremented as well. The reason I want to do this is that changes to a field on the Bond, or on an object it refers to, means that the value of that Bond needs to be recalculated. This can be a costly operation, and so I'd like to make sure I do it only for updated ones.

It seems to me that this could be done with a combination of the Lifecycle interface (on Bond load, make a note of the versions of the referenced entities), and the Interceptor (onFlushDirty of a Bond, check if the versions of any referenced entities have changed since load. If so, mark dirty).

Or possibly use a bidirectional link from referenced entities such as Coupons back to the containing Bond, and use the Interceptor.onFlushDirty() to make all such strictly 'contained' entities notify their containers when they get modified, which would probably cause incrementing of the container version.

My main question has to do with best practices for this kind of situation. Our object hierarchy has many levels... this example of Bond and Coupons is one of the simpler ones, but shows my basic problem. I'm

- If you have an polymorphic object reference, where that referred object is strictly contained by the referring object, do you have to model the reference as an entity reference? (I agree with you, pretty sure the answer is yes)
- Assuming you should model the above example as an entity, is there a good way to ensure proper version incrementing?


Top
 Profile  
 
 Post subject:
PostPosted: Mon Sep 17, 2007 11:36 pm 
Beginner
Beginner

Joined: Thu Jun 17, 2004 11:25 pm
Posts: 21
Location: Los Angeles
Versioning is meant to detect changes on that row of the database only, in order to avoid update collisions. It's not really proper to overload with the data caching kind of thing you are talking about. If you are saving the value of the bond in the database, then the version will be updated when you do the recalculation. If you are NOT saving the value of the bond in the database (my recommendation) then you shouldn't update the version number of the bond just because its value changed.

I'd recommend Coupons have a link to the Bond that owns them. The Bond has a nullable value field. All the setters on a Coupon that affect the Bond's value null out the Bond's value (give special attention to the setter that sets the Coupon's Bond, as it needs to null out both the old bond and the new one). The Bond.getValue() returns the saved value if it is not null, otherwise getValue() computes the value and saves it before returning it.

This has the advantage of ensuring getValue() works properly during the period between when the Coupon is updated in Java and when the Session is flushed.

=Jeremy=

P.S. if you like this answer, please rate this post.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Sep 18, 2007 12:36 am 
Newbie

Joined: Fri Sep 14, 2007 2:02 pm
Posts: 10
Location: New York, NY
Fair point about the version field, we're definitely overloading the intent of it.

Values are strictly transient, derived from the bond definition. Values are in fact not even accessed on the Bond, so neither Bonds nor Coupons themselves could invalidate them.

One way to think about the values is that they're cache entries. Part of their key is the Bond ID and version, along with some other environmental state. In this context, your suggestion translates to fact that the cache key should be the state of the Bond, not just its ID and version, which makes a lot of sense.

Thanks for your help!


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