I have the following class structure:
A is abstract and mapped.
B inherits from A and is abstract, but has no map.
C inherits from A and is concrete and mapped as joined-sublass.
D inherits from B and is concrete and mapped as joined-sublass.
E inherits from B and is concrete and mapped as joined-sublass.
F inherits from E and is concrete and mapped as joined-sublass. It has a property that is a collection of A's, and another that is a collection of B's.
G is concrete class that has a property of type B, and is mapped. It does not inherit from anything other than Object.
Note that the B class is NOT mapped. This is at the root of my problem. It is not mapped to a database table because it doesn't add data to A, just behaviour.
I have the following (somewhat obvious) table structure:
TA is a table that has 1 row for each A object.
TC is a table that has 1 row for each C object and is matched to a row in TA.
TD is a table that has 1 row for each D object and is matched to a row in TA.
TE is a table that has 1 row for each E object and is matched to a row in TA.
TF is a table that has 1 row for each F object and is matched to a row in both TA and TE.
TFA is a linking table that has a row for each object in the A property of F.
TFB is a linking table that has a row for each object in the B property of F.
TG is a table that has a row for each G object. It has a column for the ID of the B object it refers to, which will have a match in TA as well as one of TC, TD, or TE (and possibly TF).
Ok so far? My problem is this. When I try to map the B property of G, it fails. It throws a CGLIB exception:
> org.hibernate.PropertyAccessException: exception setting property value with CGLIB
I can't map the B property of G as a B because B is not mapped, so I map it as an A. Since A and B are abstract, I know that I am really getting back a C, D, E, or F. These all extend B, so you may think I'd be good to go, right? Wrong. For some reason I don't understand, hibernate is creating a proxy object that is only a proxy for A.
Funnily enough, I don't seem to get a problem mapping the collections on class F. I map the collection of As as set of As and collection of Bs as set of As, and everything seems to work fine. I cast the elements of the set to a B and the cast succeeds.
Here are just some of the things that have been considered and/or tried to fix this:
Relax the interface on G to accept an A, and then cast it to B. Fails on a casting exception, since this is actually a proxy object.
As above, but also force A not to be lazy loaded. Works, but loads virtually the entire database thanks to the collections in F. Not a practical solution.
Introduce a UserType that returns a B, but is actually a C, D, E, or F. But creating those other objects is complicated. Could we use hibernate to create the object? Do we still need a UserType then?
Forget about Hibernate when creating a G and use raw JDBC. Once you get back the ID of the B, feed it to hibernate and use the concrete class returned.
Create a table for B that holds only IDs. Our DBA rightly balks at a hack like this, since the only purpose of the table would be to satisfy the demands of our tool.
Store the B in G as a set. This would require another bogus table, though, as well as altering the behaviour of G in an odd way.
Change all the joined-subclasses to subclasses that use joins. As I understand it, this wouldn't support the collections on F, though.
So am I understanding the problem correctly? If not, what am I missing? If I do understand the problem, what do people recommend as the solution? Is this actually a hibernate bug? Is there a workaround? Any help would be appreciated.