JPA 2.1 and Table Inheritance
Newly I came across a little problem with JPA 2.0 and table inheritance: what if we have a base entity and extend it to some other subclasses and use inheritance strategies? Does it work out of the box or do we need some configuration?
Well, in this article I’ll take a look at this problem with the Hibernate JPA 2.1 implementation.
Setting up the project
To have a project we need some entities, data in the database (to see the results actually) and the application where everything is configured. The base project will be my example created in a previous article.
The database I’ll use is a simple file based H2. However if I encounter some problems while I’m writing this article I’ll switch to another one. And to trigger the database queries I’ll add a simple button to a website and display some results there.
Let’s define the entities. This is always a bit problematic because in real life projects you have so good entity definitions which you cannot port to an article. So for this I’ll stuck to the common inherited object types we have in example applications and projects: shapes, vehicles and persons. For the whole article I’ve chosen the shapes and I’ll guide you through these types with the help of basic shapes.
Just because there are three main type of table inheritance in JPA 2.1:
- Single Table
- Table Per Class
I warn you that I may have mis-used some of the entities and they would fit into another inheritance type better but this is how I’ll show you this feature.
Let’s see the entities and the database definition we will use for each inheritance type.
The base class is Shape with one field: idString which is an ID with the type of a String. The children classes are Triangle (with a Boolean field to tell that every side is same long) and a Circle (which has its radius stored as an Integer).
Basic examples but I only want to show how inheritance among entity objects (and database tables) work.
As for Hibernate if you do not specify names for tables, columns, discriminator — or anything which can have a user defined name, Hibernate takes the class name to create the table and the name of the field and uses it as the column name. So for this, if you plan to use another way to create the database schema (so not with Hibernate’s create-drop) it is a good idea to annotate the classes and the fields accordingly. As for me a good practice is to annotate every field, even if it gets the same column name as the field itself. However in the examples I’ll stick to the Hibernate-version if you’d use the auto-creation function.
For other special naming conventions I’ll refer where I talk about the field.
This will be the first one.
Joined inheritance is like real “OO-life” inheritance: you have your base class and the children classes in the code — and you have your base table and your children tables in the database. The one common thing in this type of inheritance among the tables is the id.
So this means that every table has the fields of the defining class as a column and the ID as an extra column beside their own fields. This is slack and fine. It is defined in the inheritance model which class extends which one. So they can be joined with their IDs. Yes, multiple inheritance would be bad in this case.
The tables definitions look as follows:
create table shapejoined (idString varchar(255 char) primary key); create table trianglejoined(idString varchar(255 char) primary key, equalsites boolean); create table circlejoined(idString varchar(255 char) primary key, radius number);
The different entities are identified by their ID. So the base class’ table has the ID and the children classes have the same ID. This helps Hibernate to identify which type has the child if it queries the database.
So if you make a query on the base class, Hibernate sends a big query to the database which joins all possible children together to get the information it needs to create the objects to return.
Single Table inheritance
In the second example we will have only one database table:
create table shape_singletable(idString varchar(255 char) primary key, type varchar(25 char), equal_sites boolean, radius number);
The main disadvantage of this version is that you can have a lot of “null” fields in the database — and you cannot have “not null” constraints for fields in children because they are stored in the same table for other types too which do not declare those fields.
Table Per Class inheritance
create table triangle_perclass(idString varchar(255 char) primary key, type varchar(25 char), equal_sites boolean);
create table circle_perclass(idString varchar(255 char) primary key, type varchar(25 char), radius number);
The main disadvantage of this type is that you can have much redundant data if the base class has many common fields. Another disadvantage is that this type of inheritance yields a warning (at least in JBoss DevStudio) and it says that this strategy is not portable and is eventually not supported by the JPA 2.0 provider. Fortunately Hibernate supports this.
The advantage of this inheritance type is that for children entities you do not need any join or discriminator column or anything else: Hibernate gets the data right out of the right table.
However if you make a select on the parent class, you can end up with a big union of all children classes to have the result:
select shapepercl0_.idString as idString1_3_, shapepercl0_.radius as radius1_1_, shapepercl0_.equalSites as equalSit1_6_, shapepercl0_.clazz_ as clazz_ from ( select idString, radius, null as equalSites, 1 as clazz_ from CirclePerclass union all select idString, null as radius, equalSites, 2 as clazz_ from TrianglePerclass ) shapepercl0_
The repository is a simple one. It has some methods to query all three superclasses and two generic methods to save and load a specific entity. This is the minimalist approach you’ll find along the internet — without the usage of extra frameworks (such as Spring Data for example). As I mentioned in an earlier article, it can lead to problems if you access the EntityManager directly to query objects from the database. So consider this repository to have the same features: you should access the database only through a custom repository.
Page and trigger
The example project I’ve set up from the WildFly archetype comes with JSF as a GUI implementation. So I used JSF to have a simple testing site where you can load the test data into the database and then run the test cases. If you have built and deployed the application to your WildFly server, and the server is running, you can call the test site on http://localhost:8080/example/test.jsf. The buttons talk for themselves. One thing you need to do is to insert the test data — without it you won’t get an exception but no results either.
This is a Hibernate bug in JIRA which reports a problem with discriminator columns and discriminator values with the Joined inheritance. This is really seldom to come but I’ve seen it with a Hibernate 4.2.0 — however only once on a Friday at work. At the weekend I made a sample application (the parent of this article’s example) and tried to get the same exception — but it did not came. I played along one day to have the same result but not. On Monday I went into the office and tried it again — and the exception was gone. I do not know how it happened but the problem disappeared.
Nevertheless I’ve found a solution on Friday too: we had to change the Hibernate version of JBoss to a newer one (4.2.15.Final) and add some parameters to the persistence.xml file and then it worked. Naturally on Monday I’ve removed those configurations and reverted the Hibernate version and it worked. So sometimes there are mysteries in the programming world too. If you are interested in the solution’s properties, take a look at the JIRA ticket, it is explained there. Or just ask me simply.
There is no non-plus-ultra solution if you have to map inheritance into relational databases. The JPA 2.1 specification gives you three methods and you have to consider which one to use. If you have few fields across all entities and your tables/classes will not change then you should use a single table. Otherwise it is good to have a joined strategy. Table per class has too much redundant data (another field than the ID is much) which makes things a bit complicated — if you need to change live data in the database directly than more.
However I do not suggest you should use my suggestions. It is up to you and I’ve shown all three strategies. Now it’s up to you to choose.
And I worked on project where we mixed joined and single table — as I told you it depends on what you need and how much data you’d store in the database. The thirds (and in big business systems the main) aspect is the performance. Sometimes you have to sacrifice one approach because it is slow and another would be faster.
As most of the time this article has its source code at my GitHub repository in the inheritance branch. Each of the table-examples is separated in their own packages; the DDL script is added too however by default Hibernate’s auto create-drop strategy is used. And you can find test data as one SQL file there too.