I've been working with Hibernate for a week or so now migrating an existing persistence framework and I came across an issue. Does Hibernate have a built in mechanism for creating proxies which are of the correct type in a polymorphic association? For example, if I have the following classes:
Code:
public class Role {
public Entity getEntity() {...}
public void setEntity(Entity entity) {...}
}
public class Entity {}
public class Person extends Entity {}
public class Organization extends Entity {}
with the following mappings:
Code:
<class name="Role" table="Roles">
<id .../>
<many-to-one name="entity" column="entityId" class="Entity"/>
</class>
<class name="Entity" table="Entities" lazy="true">
<id .../>
<joined-subclass name="Person" table="Persons" lazy="true">
<key .../>
</joined-subclass>
<joined-subclass name="Organization" table="Organizations" lazy="true">
<key .../>
</joined-subclass>
</class>
I would like for the proxy to the Entity to be of the correct type (i.e. either Person or Organization) when I load the Role from the database. I realize that something similar can be done using interface proxies. However, my understanding (and please correct me if I'm wrong) is that the resulting proxy would implement both Person AND Organization. I've tried this before and run into ClassCastExceptions when trying to execute certain methods on the resulting proxy. Not to mention that this would break "instance of".
Of course, I realize why this must be done this way, since there is really no simple way to determine the correct most derived type of the resulting proxy. However, I can think of 2 ways in which this could be done. Of course this is all moot if Hibernate can already do this stuff and I just haven't been able to figure out the magical incantation ;)
1) When the query to load the Role is generated, do outer joins with the Entities tables to determine the correct type to proxy. For example, the resulting query might look like:
Code:
select R.id, R.entityId, case when P.id is not null then 1 when O.id is not null then 2 when E.id is not null then 0 end as entityClass from Roles R left outer join Entities E on R.entityId=E.id left outer join Persons P on E.id=P.id left outer join Organizations O on E.id = O.id where R.id=?
Hopefully that's not too messed up, my SQL is not great ;) The obvious problem with that is that it does a lot more joins that would be necessary if we wern't trying to fetch the type of the proxied class. If the class hierarchy is large this might be prohibitive. However, it might be nice if there was an option to do it. I'm sure it would be possible to implement this in Hibernate using a custom persister but I haven't got a clue how. The 2nd option I WAS able to implement with a custom persister.
2) When the proxy is created, do a query to determine the type of the class to proxy. The obvious drawback to this is that you're now doing a query to create the proxy which doesn't seem very smart. Of course it should be a pretty efficient query and would look something like this:
Code:
select case when P.id is not null then 1 when O.id is not null then 2 when E.id is not null then 0 end as entityClass from Entities E left outer join Persons P on E.id=P.id left outer join Organizations O on E.id = O.id where E.id=?
One might be able to argue that if we are going to do this query, why not just go ahead and load the Entity? This is probably a perfectly valid argument. It would be nice to at least have the option though. If the Entity class has a lot of data, this may end up being faster in practice (especially if the initialization never gets invoked). Also it provides for the ability to halt the loading of the object graph at a correctly typed proxy, for the cases where an Entity is associated with other objects. Of course, if the object does end up getting initialized, we've now done 2 queries instead of one. But at least the Entity object is of the correct type ;)
This is actually how I handled this issue in a persistence framework I built before switching to Hibernate (which is wonderful by the way, great framework). It worked very well in practice and I never had any performance issues (although we don't have a ton of users and our database is on the same machine as the application). Of course, there may be some fundamental flaw in one or both of these methods but it seems like they would work to me.
Alright, so I mentioned I was able to get this working using a custom persister (really just a custom ProxyFactory). If anyone's interested I can post the code for it here.
To the Hibernate experts, if there's another way to do what I'm trying to do which is already built into Hibernate then please let me know. If not then maybe something like this would be a good thing to add in future versions.
Thanks in advance for your time,
Eric