Monday, December 22, 2008

Mapping a class multiple times in hibernate

I'm currently working on a project where we are converting the persistence layer of a legacy system from an object database to Hibernate. Since you can save any object graph with an object database, this causes some interesting mapping scenarios. In a couple of instances, I found it very useful to use the 'entity-name' attribute, which allows you to map a single class more than once.

For example, consider the following:

class MyMapWrapper {
Map theMap;
}

class A {
MyMapWrapper wrapper;
}

class B {
MyMapWrapper wrapper;
}

How do you map MyMapWrapper? The generic types determine the way that the wrapped map should be mapped. The nice way is to unwrap the map, but when working with legacy code this can cause a lot of ripple effects that you may not want to get into. Using entity-name, the solution becomes quite simple:

<class name="MyMapWrapper" entity-name="StringToStringMapWrapper">
<map name="theMap">
<key column="id">
<map-key column="strkey" type="string">
<element column="strvalue" type="string">
</map>
</class>


<class name="MyMapWrapper" entity-name="StringToIntegerMapWrapper">
<map name="theMap">
<key column="id">
<map-key column="strkey" type="string">
<element column="intvalue" type="integer">
</map>
</class>

<class name="A">
<many-to-one entity-name="StringToStringMapWrapper"/>
</class>

<class name="B">
<many-to-one entity-name="StringToIntegerMapWrapper"/>
</class>

There are one or two caveats to this though:
1. You need to know the entity-name if you want to just persist an instance of MyMapWrapper, since the class alone does not make it unique anymore.
2. You can't use entity-name in an any mapping. I don't like any, but unfortunately it is also often required for converting legacy stuff.

Disclaimer: I did not specifically test the above mappings, it just illustrates the point, but we did solve many issues this way.