Hibernate version: 4.1.8
I call a method on a persist object that is proxied by Javassist, that simple returns "this" (to get the proxied object)
The class JavassistLazyInitializer noticed this and will return the proxy again :(... that results in a ClassCastException.
I don't think this is correct, JavassistLazyInitializer should not return the proxy, but the real return value as it results in unexpected behavior... And it's hard to find and results in nasty bugs!
Let me explain:
I have a subclass Member that extends from Person:
Code:
class Person {
Member asMember() {
throw RuntimeException("WrongType");
}
}
class Member extends Person {
Member asMember() {
return this;
}
}
I use this as an alternative to the visitor pattern, as it's usage is friendlier, easier, and performs better.
So when I am sure that the Person is a member (by a isMember() method), I call the asMember() method.
This works fine till the Member instance is proxied by Hibernate. Example: member is a field of a Company class:
Code:
class Company {
Person person;
}
When Company.member is lazy loaded and I call company.person.asMember() I get a
ClassCastException. I think this is a
bug, as what I do is fine and legal.
What Hibernate however does: it will proxy the field person of type Person.
When I call the method company.person.asMember(), it will call method JavassistLazyInitializer.invoke() which will return the proxy as soon as it sees that method asMember() returns the same object as wrapped by the proxy (line 200):
Code:
return returnValue == target ? proxy : returnValue;
However the proxy is a generated subclass of Person and the return type of the asMember() method is of type Member, such that ClassCastException occurs.
Conclusion: I don't think this optimization (returning the proxy) is a "good one".
I think the above line in JavassistLazyInitializer, should simple be:
Code:
return returnValue;
But I am not sure what kind of optimizations you will miss in this case.
So you could fine tune this to:
Code:
return returnValue == target && returnValue.getClass().isInstance(proxy) ? proxy : returnValue;
I tested this, and it seem to work fine...
Please some feedback on this?
BTW, Workarounds:
a) remove the method, and use a "real" visitor object...
b) Return the object in a Wrapper object such that JavassistLazyInitializer won't see it as the same object as the target, such that it will return the "real" return value...