-->
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.  [ 19 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: proxies, CGLIB enchancing, inheritance and confusion
PostPosted: Tue Feb 21, 2006 2:34 am 
Regular
Regular

Joined: Fri Oct 01, 2004 2:19 am
Posts: 111
Location: Melbourne, Australia
Hibernate version: 3.0.5

I have a baffling (to me at least :)) situation: Consider the following
class hierarcy:
Code:
class A { int id; }
class B extends A { String name; }

class SomeClass { A refA; }
class SomeOtherClass { A refB; }



and the database has the following tables and data:
Code:
TABLE A: ID     REF_TYPE NAME
         1100   B        SomeName

TABLE SOME_CLASS: ID A_ ID
                  1000  1100

TABLE SOME_OTHER_CLASS: ID   B_ID
                        1200 1100


and the following instance set after some queries:
Code:

ObjectOfA : A$$EnhancedByCGLIB$$234332@1100

ObjectOfSomeClass : SomeClass@1000
  refA = ObjectOfA


For the purpose of this discussion, ObjectOfSomeClass is an instance of SomeClass which was loaded as a result of a load/query and its refA
member points to ObjectOfA, an instance of *B* was loaded
and was initialised as a proxy so the real class is the CGLIB enhanced
class.

Now, when an instance of SomeOtherClass is required, and when the
instance has its refB member pointing to the same instance of B that is
in the session cache, the same reference value (1100) is being set
in the instance - that is the enhanced class version.

This is not a problem except when the following takes place:

Code:
B someB = (B)ObjectOfSomeClass.getRefB();


The cast fails since the actual type is the enhanced class.
To compound the confusion, sometimes the reference is the
actual type - B.

What gives? I would have thought that since the correct mapping is
provided, whenever a reference to B in an instance of SomeOtherClass
is made, interception will ensure that the actual target instance is
returned.

What am I missing? Any help will be greatly appreciated

_________________
Cheers,

Bonny

please don't forget to rate :)


Top
 Profile  
 
 Post subject:
PostPosted: Tue Feb 21, 2006 3:06 am 
Newbie

Joined: Sat Feb 18, 2006 5:30 am
Posts: 17
Hibernate caches proxies by entity's primary key. If in some query you retrieved instance of SomeClass, where refA is initialized to lazy-loading proxy pointing to instance of class B, all other queries, that must return same instance of B, will instead return same proxy of type A.

I reported same issue today on this forum:
http://forum.hibernate.org/viewtopic.php?t=955817

You may workaround this issue by
1) Using non-lazy associations
2) Using bytecode instrumentation (helps only when walking by associations in mapped classes)
3) Using explicit proxy unwrapping (see interface HibernateProxy)
4) Mix all above


Top
 Profile  
 
 Post subject:
PostPosted: Tue Feb 21, 2006 6:12 pm 
Regular
Regular

Joined: Fri Oct 01, 2004 2:19 am
Posts: 111
Location: Melbourne, Australia
cryozot wrote:
Hibernate caches proxies by entity's primary key. If in some query you retrieved instance of SomeClass, where refA is initialized to lazy-loading proxy pointing to instance of class B, all other queries, that must return same instance of B, will instead return same proxy of type A.

I reported same issue today on this forum:
http://forum.hibernate.org/viewtopic.php?t=955817

You may workaround this issue by
1) Using non-lazy associations
2) Using bytecode instrumentation (helps only when walking by associations in mapped classes)
3) Using explicit proxy unwrapping (see interface HibernateProxy)
4) Mix all above


This seems a pretty fundamental problem for models with inheritance
hierarchies and code that performs visitation over the model,
expecting to find certain types. I hope some Hibernate team member
would shed some light on the issue.

As to the work arounds, 1 and 3 above are not really viable in my case
(for 1 because this would pull back huge chuks of my model into memory,
and for 3 because most of the code in question is oblivious to the
persistence aspects of the application in general and Hibernate in
particular). Would you elaborate a bit on point 2 ?

_________________
Cheers,

Bonny

please don't forget to rate :)


Top
 Profile  
 
 Post subject:
PostPosted: Tue Feb 21, 2006 11:51 pm 
Newbie

Joined: Sat Feb 18, 2006 5:30 am
Posts: 17
Hibernate contains bytecode instrumentation tool, which enhances persistent classes in a way, that allows to overcome some of the inheritance mapping issues. As I understand, it intercepts access to persistent fields, loads actual object of correct type, and replaces proxy in that field. This allows you to traverse associations in natural way, avoiding crosscutting of the persistence aspect.

See org.hibernate.tool.instrument.InstrumentTask class for futher reference.

Unfortunatety, this approach works only for associations in persistent classes. If you query domain objects by Session.createQuery(), bytecode instrumentation is unable to help you.

I can't see right solution for this problem, that will work in general case. Alas, programming is always related to trade-offs between tools and imagination :)

BTW, the other workaround is to break polymorphic association in persistent classes and use DAO to retrieve associated object directly. Not very nice, but works well.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Feb 21, 2006 11:55 pm 
Newbie

Joined: Sat Feb 18, 2006 5:30 am
Posts: 17
Hibernate contains bytecode instrumentation tool, which enhances persistent classes in a way, that allows to overcome some of the inheritance mapping issues. As I understand, it intercepts access to persistent fields, loads actual object of correct type, and replaces proxy in that field. This allows you to traverse associations in natural way, avoiding crosscutting of the persistence aspect.

See org.hibernate.tool.instrument.InstrumentTask class for futher reference.

Unfortunatety, this approach works only for associations in persistent classes. If you query domain objects by Session.createQuery(), bytecode instrumentation is unable to help you.

I can't see right solution for this problem, that will work in general case. Alas, programming is always related to trade-offs between tools and imagination :)

BTW, the other workaround is to break polymorphic association in persistent classes and use DAO to retrieve associated object directly. Not very nice, but works well.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 22, 2006 12:29 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 3:00 pm
Posts: 1816
Location: Austin, TX
The runtime proxies generated by Hibernate provide *polymorphic* behavior (and no, typecasting does *not* constitute polymorphic behavior nor does instaceof). Consider the low-level details here; when you load your ObjectOfSomeClass which has a lazy polymorphic association to the A/B hierarchy, Hibernate needs to put *something* into the loaded ObjectOfSomeClass. But, as it does not yet have access to the data relating to the A/B association (remember, it is lazy) the best it can do is a proxy typed to the root of the hierarchy


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 22, 2006 2:29 am 
Regular
Regular

Joined: Fri Oct 01, 2004 2:19 am
Posts: 111
Location: Melbourne, Australia
Steve,
steve wrote:
The runtime proxies generated by Hibernate provide *polymorphic* behavior (and no, typecasting does *not* constitute polymorphic behavior nor does instaceof). Consider the low-level details here; when you load your ObjectOfSomeClass which has a lazy polymorphic association to the A/B hierarchy, Hibernate needs to put *something* into the loaded ObjectOfSomeClass. But, as it does not yet have access to the data relating to the A/B association (remember, it is lazy) the best it can do is a proxy typed to the root of the hierarchy


I get this, I think - Hibernate attempts to save a trip to the DB and so
it uses only the ID which is used as an FK in the referring object and
whatever meta data information it has about the type of the reference,
here the base of the hierarchy.

OK, so there's no way for Hibernate to determine the right proxy type
during the first reference action, and subsequently, the target of the proxy
is indeed set to an object of the right type. Also, I did not mean to imply
that the Hibernate proxies are not exposing polymorphic behaviour or that
invoking methods on the objects is not the way to do it.
However some of my methods use the base class versions of these objects, in order
to allow all classes in a hierarchy to be manipulated. Some such
manipulation is obviously type specific and so down casting is required.
Moreover, the code is completely persistence agnostic and has no
way of knowing that the objects are really proxies, nor does it care.

So what is a recommended solution while working with persistent objects
like this? How do I get to the target of the proxy, which is of the RIGHT
type without interspersing the code with Hibernate proxy manipulation methods?

_________________
Cheers,

Bonny

please don't forget to rate :)


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 22, 2006 2:48 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 6:10 am
Posts: 8615
Location: Neuchatel, Switzerland (Danish)
lookup visitor pattern at hibernate.

_________________
Max
Don't forget to rate


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 22, 2006 8:53 am 
Regular
Regular

Joined: Fri Oct 01, 2004 2:19 am
Posts: 111
Location: Melbourne, Australia
max wrote:
lookup visitor pattern at hibernate.


I did, but it seems excessive to just do this conversion. Imagine this bit
of code:
Code:
Path path = loadPath();
Port localPort = path.getLocalPort();
Port remotePort = path.getRemotePort();
EdgeRouter router = (EdgeRouter)localPort.getNe();
CoreRouter router = (CoreRouter)remotePort.getNe();


In the above example, a path can connect any two types of NEs
and the code above is typical of several code fragments that, depending
on use case, would be manipulating different kinds of NEs and it would
be a lot less maintainable if the code needed to invoke the visitor
pattern (which is used extensively in other parts of the application)
just to coerce to references to be of the right type.

Is there a way to get Hibernate to attempt to resolve the actual type
by using the database (I do not mean by way of setting proxy = false,
as this would load large chunks of the database into memory) ?

_________________
Cheers,

Bonny

please don't forget to rate :)


Top
 Profile  
 
 Post subject:
PostPosted: Wed Feb 22, 2006 9:45 am 
CGLIB Developer
CGLIB Developer

Joined: Thu Aug 28, 2003 1:44 pm
Posts: 1217
Location: Vilnius, Lithuania
"Moreover, the code is completely persistence agnostic ... "
It is not so practical, there are some limitations like this or multithreading, code cant not be 100% "transparent" in practice.

"So what is a recommended solution while working with persistent objects
like this? "
I prefer to map "discriminator" as simple property.


Top
 Profile  
 
 Post subject:
PostPosted: Sat Feb 25, 2006 12:26 am 
Newbie

Joined: Sat Feb 18, 2006 5:30 am
Posts: 17
BTW, you can weave resolving of lazy many-to-one references in the persistence agnostic code by using AOP approach. It's easy to do it via AspectJ or something similar like JBoss AOP Framework. The latter one is very useful approach, because you can develop and test your persistence agnostic code within conventional IDE, but let container to weave in persistence-specific code at load time. We have successfuly prototyped such technique and currently consider applying it in the production code.


Top
 Profile  
 
 Post subject: Question regarding previous conversation.
PostPosted: Tue Oct 03, 2006 3:44 pm 
Newbie

Joined: Tue Oct 03, 2006 3:35 pm
Posts: 9
Location: São Paulo
I fetch

String query = "from SomeOtherClass c where c.refB.class = my.refB.subClass"

Shouldn't CGLIB and Hibernate have enough info to proxy the association as a being a refB?


Cheers,


:Luiz.


PS: Another question:

Why not fetch the discriminator by default for Entities mapped in a tree?
this whould enable to proxy to the exact subclass allowing casting at runtime wouldn't it?


Top
 Profile  
 
 Post subject: Re: Question regarding previous conversation.
PostPosted: Thu Oct 05, 2006 10:41 pm 
Newbie

Joined: Sat Feb 18, 2006 5:30 am
Posts: 17
luizDecaro wrote:
I fetch

String query = "from SomeOtherClass c where c.refB.class = my.refB.subClass"

Shouldn't CGLIB and Hibernate have enough info to proxy the association as a being a refB?


No. AFAIK, Hibernate is unable to extract type information from such HQL queries. There is simply no place, where it can be stored.

Anyway, constraining type of fetched entity using query doesn't imply, that you will use associated entity in your code. If this is not the case and you indeed want to follow refB association after fetching c, then you can eagerly fetch it with along with c, using JOIN FETCH clause. In this case, refB will refer to instance of valid class.

luizDecaro wrote:
Why not fetch the discriminator by default for Entities mapped in a tree?
this whould enable to proxy to the exact subclass allowing casting at runtime wouldn't it?


I'm afraid, eager fetching of discriminator values for associated entries will slow down query execution the same way, as if actual associated entities are fetched eagerly, therefore, eliminating benefits of lazyness. Especially, if inheritance hierarchy is mapped using joined-subclass instead of discriminator values. So, field-level "no-proxy" lazyness is the only performance-efficient solution - of course, at the cost of additional postprocessing step.


Top
 Profile  
 
 Post subject: Few considerations.
PostPosted: Fri Oct 06, 2006 9:46 am 
Newbie

Joined: Tue Oct 03, 2006 3:35 pm
Posts: 9
Location: São Paulo
Tnx for your reply mate.

Actually, maybe the Hibernate should be able to gather this kind of info from queries !

But i'd like more to have an enhancement to provide typeCasting in a cenario where you have:

.polymorphism on a table-per-hierarchy mapping.
.fetching the id and the discriminator in the the proxied associated entity.
.Would this be as bad as you pointed? Bring just a discr field together with an id?

I think it would be better than using a field "no-proxy" cos' after a typeCasting you get a full load of your proxied entity.


I'm i fooling away in all this? Am i lacking any concept?


Cheers,


:Luiz.


Top
 Profile  
 
 Post subject: Re: Few considerations.
PostPosted: Sun Oct 08, 2006 9:58 pm 
Newbie

Joined: Sat Feb 18, 2006 5:30 am
Posts: 17
luizDecaro wrote:
But i'd like more to have an enhancement to provide typeCasting in a cenario where you have:

.polymorphism on a table-per-hierarchy mapping.
.fetching the id and the discriminator in the the proxied associated entity.
.Would this be as bad as you pointed? Bring just a discr field together with an id?

I think it would be better than using a field "no-proxy" cos' after a typeCasting you get a full load of your proxied entity.


Such functionality can be easy achieved, if you will mark your associations as lazy="none" :)

From the RBDMS standpoint there is almost NO DIFFERENCE, between fetching one field and fetching all fields. Important point here is that determining type of the associated entity in table-per-hierarchy mapping requires joining additional table, which is infeasible from performance standpoint in most cases. Note, that when Hibernate loads entity from DB, it only queries tables, related to that entity's class. Tables, related to lazily associated entities are not even touched.

In other cases, as I said, lazy="none" or JOIN FETCH clause can be used.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 19 posts ]  Go to page 1, 2  Next

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.