Inheritance in JPA

Relational databases have tables and columns and object-oriented programming languages have classes and fields but they also provide inheritance. A concept that is not known in relational databases.

JPA provides solutions to map inheritance to database tables but by default JPA doesn’t care about inheritance and will not map any fields from a superclass to the database.

You always have to define how inheritance should be handled. JPA distincts two scenarios:

Scenario 1: Mapped Superclass

Often you want to define fields and/or behavior in a common superclass like a timestamp when the entity was created:

BaseEntity
Unless you don’t define how this should be handled, JPA will not consider the field createdAt when persisting the Address entity.

To tell JPA to include the fields of a superclass in the mapping you have to use the MappedSuperclass annotation:

@MappedSuperclass
public class BaseEntity {
    private Timestamp createdAt;
}

@Entity
public class Address  extends BaseEntity {
}

In that case JPA assumes that the database table Address contains a column with the name createdAt.

Scenario 2: Mapping Inheritance

Let’s assume that we have the following inheritance hierarchy:

Inheritance
The Employee class is abstract and has a field name, where as FulltimeEmployee doesn’t have its own fields but ParttimeEmployee has a field that defines the percentage he works for the company.

But how can we map this with JPA? Let’s have a look what the specification says:
http://download.oracle.com/otndocs/jcp/persistence-2_1-fr-eval-spec/index.html

2.12 Inheritance Mapping Strategies

The mapping of class hierarchies is specified through metadata.

There are three basic strategies that are used when mapping a class or class hierarchy to a relational database:

  • a single table per class hierarchy
  • a joined subclass strategy, in which fields that are specific to a subclass are mapped to a separate table than the fields that are common to the parent class, and a join is performed to instantiate the subclass.
  • a table per concrete entity class

An implementation is required to support the single table per class hierarchy inheritance mapping strategy and the joined subclass strategy.

Support for the table per concrete class inheritance mapping strategy is optional in this
release. Applications that use this mapping strategy will not be portable.

Support for the combination of inheritance strategies within a single entity inheritance hierarchy is not required by this specification.

Metadata

To activate inheritance we have to add the Inheritance annotation to the base class Employee:

@Inheritance
@Entity
public abstract class Employee {
    @Id
    private Integer id;
    private String name;
}

@Entity
public class FulltimeEmployee extends Employee {
}

@Entity
public class ParttimeEmployee extends Employee {
    private int percentage;
}

Database Representation

OK…. but how does the database tables look like?

The Inheritance annotation has an attribute strategy of type InheritanceType. There are three inheritance types like described in the spec: SINGLE_TABLE, JOINED, TABLE_PER_CLASS.

SINGLE_TABLE

The default strategy is SINGLE_TABLE and would look like this:

Single Table
As you can see all attributes of the whole inheritance hierarchy are flattened to a single table. Additionally a column dtype was added. (The column name is not defined in the specification and is therefore implementation dependent.) This column indicates which class must be instantiated when loading the data from the database.

The advantage of this strategy is that it’s easy to use even if the user directly uses the database and doesn’t know about inheritance in the entity model.

But there is also a disadvantage. Have a look at the column percentage. The N indicates that this column is nullable. But wait a ParttimeEmployee must always have a percentage! That’s not possible because the other subtype of Employee FulltimeEmployee doesn’t have this field.

So if you have mandatory fields in subclasses SINGLE_TABLE is probably not the best strategy.

JOINED

The JOINED strategy will create a table for every class in the hierarchy:

Joined
This looks like our class diagram, doesn’t it? The id is inherited means that for every instance of either FulltimeEmployee or ParttimeEmployee we will have a record in the Employee table and the primary key of Employee will be the primary and the foreign key in the FulltimeEmployee or ParttimeEmployee table.

And in contrast to the SINGLE_TABLE strategy the percentage column in the ParttimeEmployee  is now not nullable!

Perfect, but is there a disadvantage? Yes, the JPA implementation could always join the tables and this could be slower than the SINGLE_TABLE strategy. But this depends on the database and the indexes that are set on the tables.

Also querying the database with plain SQL could also be a bit more complicated because you have to do the same joins that the JPA implementation would do to get all the data.

TABLE_PER_CLASS

The last strategy is called TABLE_PER_CLASS because there is only a table if the Entity class is not abstract. In our example the table Employee is missing because the class Employee is abstract.

Table per Class
In the first place this strategy looks pretty good as it also allows to have the percentage column not nullable in the database table. But the disadvantage is when you query on the superclass level Employee then it has to execute two queries or do a union of FulltimeEmployee and ParttimeEmployee that could lead to a performance issue.

Conclusion

If you are using inheritance with JPA you can choose between the mapped superclass to have all fields of the superclass in the database table of the entity or to use “real” inheritance with one of the three inheritance strategies.

In my opinion the TABLE_PER_CLASS strategy is not useful and with the disadvantage of multiple queries or a union probably the slowest option.

Choose SINGLE_TABLE when you subclass have only a few fields that are not mandatory. Also choose this option when you use the database not only with JPA but also with plain SQL because this has the simplest model for querying.

In all other cases go for the JOINED strategy.