Tuesday, August 26, 2008

Hibernate Inheritance Mapping Strategies

If you've played with Hibernate for a while, one thing you'll really appreciate is how it helps you avoid the "object/relational mapping" paradigm mismatch.

Now, this is great, because it frees you up to focus on getting your object hierarchy / object models right, without (too much) focus on the underlying persistence layer. However, this doesn't come completely for free.

Inheritence is one area where the mistmatch between object-oriented and relational world becomes particularly visible. What this means is that you have to undertake some thought when it comes to mapping classes to tables, in the case where the classes exhibit inheritence.

A number of references (for instances, I read about this in Chapter 3 of the excellent book "Hibernate In Action") talk about the "three different approaches to representing an inheritance hierarchy", as classified in a paper "Mapping Ojects to Relational Databases" by Scott Ambler in 2002. These are:
  • Table per concrete Class ("Table per class") - One table is used for each (non-abstract) class.
  • Table per class hierarchy ("Single table per class hierarchy") - One table is used to map an entire class hierarchy.
  • Table per subclass ("Joined subclasses")- One table is used for every subclass (including abstract classes and interfaces). These only contain columns for the non-inherited properties.
Each of these is mapped (using Hibernate annotations) using the @Inheritance annotations. That is, @Inheritance(strategy=InheritanceType.TABLE_PER_CLASS), @Inheritance(strategy=InheritanceType.SINGLE_TABLE) and @Inheritance(strategy=InheritanceType.JOINED) respectively.

However, recently I found about another way of handling inheritance in my object model being mapped to the persistence layer. It is useful for the case where you have a superclass that does not need to have any corresponding tables in the underlying database, and does not need to be queryable. In this case, a superclass can be annotated with the @MappedSuperclass annotation.

I thought I should mention this, as most of the information about the three strategies for inheritance doesn't seem to mention this as a "fourth" option - probably because in this case the superclass is not actually an entity.

In fact, I had a bit of difficulty understanding the difference between @MappedSuperclass and the @Inheritance(strategy=InheritanceType.TABLE_PER_CLASS) strategy. They are, in fact, very similar.

@MappedSuperclass reference states:

"A mapped superclass designates a class whose mapping information is applied to the entities that inherit from it. A mapped superclass has no separate table defined for it."

So, the MappedSuperclass annotation is similar to "table per class" inheritance, but (the difference is) it does not allow querying, persisting or relationships to the superclass. Table per class does allow these (but, if I understand correctly, is fairly inefficient at these compared to the other two inheritance strategies.)

Thus:
  • Mapped superclasses can't be targets of entity relationships
  • They can be either abstract or concrete. (Some references suggest that a mapped superclass normally should be an abstract class).
  • A mapped superclass is not an Entity

More information on these can be found at http://en.wikibooks.org/wiki/Java_Persistence/Inheritance#Mapped_Superclasses

The advantage of using MappedSperclass annotation for me, at this time, is that it seems the simplest approach if I don't want the superclass to be queried or relationships going directly to it. For the top level of my hierarchy this seems to be a good way to go.

No comments: