-->
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.  [ 9 posts ] 
Author Message
 Post subject: Wrong proxy object returned from getter using joined-subclas
PostPosted: Tue Nov 28, 2006 10:30 pm 
Newbie

Joined: Sat Nov 18, 2006 2:21 pm
Posts: 6
Location: California
I have an inheritance hierarchy using joined-subclasses. In the root class (Lets call that A), I have a member "nextA" of type A. So I can create a chain for example:

a1.nextA->a2.nextA->a3.nextA->null

defined such:

<many-to-one name="nextA" class="A" column="nextA" lazy="no-proxy"/>

The no-proxy argument doesnt work because I am not doing build time instrumentation.

I have many subclasses of A, such as B and C.

If I now new up a chain of B's for instance:

b1.nextA->b2.nextA->b3.nextA->null

If I now retrieve b1 and then call nextA, I will get an instance of A$$EnhancedByCGLib...... This is the proxy class for A and does not have the extra fields of b nor carry the behavioral differences encoded in my carefully crafted lines of java :-}

This breaks of course one or two rules of inheritance. I currently have a hack where I know from the base type, its true type and then test to see if it is the type I really expect. If not I will then re-retrieve it.....I am of course not very satisfied with this solution.

Am I doing something wrong? I looked into this a few weeks ago and recall reading something about this with the final comment of "It really isnt that bad"....well in my case it is. I have behavior in B that is different than A, I use an object oriented language for a reason :-}

If I do build time instrumentation, is it going to resolve this issue? This seems like a major issue.


Any clues much appreciated.


Hibernate version:
3.2

Mapping documents:
joined-subclass

Name and version of the database you are using:
mysql


Top
 Profile  
 
 Post subject:
PostPosted: Wed Nov 29, 2006 7:29 am 
Senior
Senior

Joined: Mon Oct 23, 2006 5:12 am
Posts: 141
Location: Galicia, Spain
Man, i think you have a problem with inheritance concepts.

If you write

-----------------------------
class A{

A nextA;
void methodA();

}

class B extends A{
void methodB(){};
}
-----------------------------

You can do, of course:

A a,otherA;
B b,otherB;

a.nextA = otherA;
a.nextA.methodA();

and also

b.nextA = otherA;
b.nextB.methodA()

and also

b.nextA = otherB;
b.nextA.methodA();

But hey!, nextA is still an "A" object, so you can't never do:

a.nextA.methodB()

nor

b.nextA.methodB()


if you want to get an B from nextA you must "downcast" it:

(B (b.nextA)).methodB()

Hope this helps...

_________________
andresgr (--don't forget to rate)


Top
 Profile  
 
 Post subject:
PostPosted: Wed Nov 29, 2006 3:27 pm 
Newbie

Joined: Sat Nov 18, 2006 2:21 pm
Posts: 6
Location: California
I never said that I do a nextB....and No I am pretty confident in my understanding of inheritence. But thanks for the insult.

If I do the following in any OO language:

class A
{
private A m_next;
public A next ()
{
return m_next;
}
public void setNext(A next)
{
m_next = next;
}

String toString()
{
return "A";
}
}

class B extends A
{
String toString()
{
return "B";
}
}

if I now do:

B b1 = new B();
B b2 = new B();
b1.setNext(b1);

if I now do:

A ref = b1.next();

I can do:

if (ref instanceof A)
{
// of course we get here
}

if (ref instanceof B)
{
// and we get here because it is an instance of B, now we can cast it
B refB = (B)refB;
}

I dont expect to suddenly get an A back.

I would expect ref.toString() to return "B" not "A".

I assume the problem here is that Hibernate does not attempt to perform an any query (and I assume it would have todo an outer join of that id to figure its type out)....thus can only assume it is of the base type.

In previous OR mapping system I have worked on (proprietary). Ids, typically encode the type so the type manager will know this in advance (so you get the right type).


Top
 Profile  
 
 Post subject:
PostPosted: Wed Nov 29, 2006 6:09 pm 
Senior
Senior

Joined: Sat Nov 27, 2004 4:13 am
Posts: 137
you can do:
Code:
lazy="false" fetch="select"

in your many-to-one mapping, so on selecting each instance of A, other items of the chain are retrieved automatically of the correct type.

_________________
don't forget to credit!

Amir Pashazadeh
Payeshgaran MT
پايشگران مديريت طرح
http://www.payeshgaran.co
http://www.payeshgaran.org
http://www.payeshgaran.net


Top
 Profile  
 
 Post subject:
PostPosted: Wed Nov 29, 2006 7:38 pm 
Newbie

Joined: Sat Nov 18, 2006 2:21 pm
Posts: 6
Location: California
Thanks, the problem with this is that it does not do a lazy fetch. It certainly preserves the correct inheritance behavior but will perform badly with large chains. If this chain is long (lets say 100 hundred of objects), and I fetch the first in the sequence, then I end up retrieving all 100 in one go.

Seems that lazy mechanics in this case should really only fetch the next object when you first access the getter....rather than creating a proxy in the first place of the wrong type.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Nov 30, 2006 5:10 am 
Senior
Senior

Joined: Mon Oct 23, 2006 5:12 am
Posts: 141
Location: Galicia, Spain
Quote:
I never said that I do a nextB....and No I am pretty confident in my understanding of inheritence. But thanks for the insult.


Hey, sorry but that was not my intention.

And sorry for the "nextB", it was a type error. It should say "nextA" in all cases.

_________________
andresgr (--don't forget to rate)


Top
 Profile  
 
 Post subject:
PostPosted: Thu Nov 30, 2006 1:03 pm 
Newbie

Joined: Sat Nov 18, 2006 2:21 pm
Posts: 6
Location: California
No problem....do you understand the problem now? Its pretty frustrating, I am building a versioned set of objects, each pointer points to the next version, since there will be lots of complex semantics I wanted to build that into the base class and subclass appropriately. The issue is that these could be long chains and large objects and I dont want a degradation as I get longer version chains (read switching lazy =false).

Not for me to say how hibernate should of been implemented in this regard. I have worked on a few OR mapping systems before and all supported this (though I cant use any of them as they were proprietary :-{).

If I understand what hibernate is doing here is that its saying you have minimally a reference to an "A" in the next field, construct a proxy with the id of A and as soon as you touch it we will retrieve it. We know the id of the next object from a field within the reconstituted object, no further query is required for that. The issue is that you cant magically replace the proxy with an object of type B once you attempt to reconstitute it from disk. This is a limitation of only representing the reference as a basic integer key. So subclassing with lazy references seems to be flawed pretty badly. As I see it, this could of been solved in two ways:

1) use some kind of composite key representation with type in it. Then the hibernate type system would know to create a proxy of that type.
2) the next() would infact be a lazy load method....basically the initially reconstituted object would be a proxy of sorts, where next() would be re-written using cglib to do a fetch of the object the first time it is called.
3) take the discriminator field one step further and for any many to one or one to one, allow the definition of a descrimiator field with the ref....its similar to 1 but done through an existing known xml markup that hibernate has.

So I do have a work around, but I dont like it....that work around is that all my objects have a field called "Guid". This is basically the id plus type and some more info. I have my own type system for other purposes, when I retrieve the object via next, I have to first ask my type system to substitute if its not the correct type. The problem here is that its not transparent and results in two trips to the database to get the object....
the alternative approach is to make my next field not a ref at all and just make it a guid, then wrapper it with a call to my type system....but of course that defeats part of the purpose of hibernate and doesnt give me referential integrity.

BTW I wanted to get a support call on this, but I only have 1 question and it requires me to pay 1 1/2 k for an annual support contract which my boss wont swallow :-{ especially when I am convinced that this is the way hibernate works "get over it" :-}
C


Top
 Profile  
 
 Post subject:
PostPosted: Thu Nov 30, 2006 1:15 pm 
Senior
Senior

Joined: Mon Oct 23, 2006 5:12 am
Posts: 141
Location: Galicia, Spain
Mmm, yes, now i understand O:)

Have you tried using another inheritance strategies, and using "explicit polimorphism"?

_________________
andresgr (--don't forget to rate)


Top
 Profile  
 
 Post subject:
PostPosted: Fri Dec 29, 2006 11:29 am 
Newbie

Joined: Fri Dec 29, 2006 10:43 am
Posts: 2
The Hibernate Reference contains a very brief snippet that is similar to your situation but using Cat and DomesticCat instead of A and B. Rather than using if(obj instanceof DomesticCat) they call a method if(obj.isDomesticCat()). This is because calling a method on the proxy will then cause it to initialise. However this isn't very elegant because you are cluttering your sub-classes with redundant looking code and its up to you to remember to never use 'instanceof'. Also it fetches the whole object even though, having determined its actual class, you may then decide to do no further processing on it.

Even more confusing: if you have an abstract base class A, with two sub-classes B and C. If you iterate through the table then the proxy doesn't yet know if the object is type B or type C. If you use 'instanceof' then it returns true for both B and C !!!


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