Java Persistence/Inheritance



Inheritance is a fundamental concept of object-oriented programming and Java. Relational databases have no concept of inheritance, so persisting inheritance in a database can be tricky. Because relational databases have no concept of inheritance, there is no standard way of implementing inheritance in database, so the hardest part of persisting inheritance is choosing how to represent the inheritance in the database.

JPA defines several inheritance mechanisms, mainly defined though the  annotation or the   element. There are three inheritance strategies defined from the  enum, ,   and.

Single table inheritance is the default, and table per class is an optional feature of the JPA spec, so not all providers may support it. JPA also defines a mapped superclass concept defined though the  annotation or the   element. A mapped superclass is not a persistent class, but allow common mappings to be defined for its subclasses.

Single Table Inheritance
Single table inheritance is the simplest and typically the best performing and best solution. In single table inheritance a single table is used to store all of the instances of the entire inheritance hierarchy. The table will have a column for every attribute of every class in the hierarchy. A discriminator column is used to determine which class the particular row belongs to, each class in the hierarchy defines its own unique discriminator value.

Example single table inheritance table in database
PROJECT (table)

No class discriminator column

 * If you are mapping to an existing database schema, your table may not have a class discriminator column. Some JPA providers do not require a class discriminator when using a joined inheritance strategy, so this may be one solution.  Otherwise you need some way to determine the class for a row.  Sometimes the inherited value can be computed from several columns, or there is an discriminator but not a one to one mapping from value to class.  Some JPA providers provide extended support for this.  Another option is to create a database view that manufactures the discriminator column, and then map your hierarchy to this view instead of the table.  In general the best solution is just to add a discriminator column to the table (truth be told, ALTER TABLE is your best friend in ORM).


 * TopLink / EclipseLink : Support computing the inheritance discriminator through Java code. This can be done through using a   and the  's  's   method.


 * Hibernate : This can be accomplished through using the Hibernate  annotation.  This allows database specific SQL or functions to be used to compute the discriminator value.

Non nullable attributes

 * Subclasses cannot define attributes as not allowing null, as the other subclasses must insert null into those columns. A workaround to this issue is instead of defining a not null constraint on the column, define a table constraint that check the discriminator value and the not nullable value.  In general the best solution is to just live without the constraint (odds are you have enough constraints in your life to deal with as it is).

Joined, Multiple Table Inheritance
Joined inheritance is the most logical inheritance solution because it mirrors the object model in the data model. In joined inheritance a table is defined for each class in the inheritance hierarchy to store only the local attributes of that class. Each table in the hierarchy must also store the object's id (primary key), which is only defined in the root class. All classes in the hierarchy must share the same id attribute. A discriminator column is used to determine which class the particular row belongs to, each class in the hierarchy defines its own unique discriminator value.

Some JPA providers support joined inheritance with or without a discriminator column, some required the discriminator column, and some do not support the discriminator column. So joined inheritance does not seem to be fully standardized yet.


 * Hibernate: A discriminator column on joined inheritance is supported but not required.

Example joined inheritance tables in database
PROJECT (table)

SMALLPROJECT (table)

LARGEPROJECT (table)

Poor query performance

 * The main disadvantage to the joined model is that to query any class join queries are required. Querying the root or branch classes is even more difficult as either multiple queries are required, or outer joins or unions are required.  One solution is to use single table inheritance instead, this is good if the classes have a lot in common, but if it is a big hierarchy and the subclasses have little in common this may not be desirable.  Another solution is to remove the inheritance and instead use a , but this means that you can no longer query or have relationships to the class.


 * The poorest performing queries will be those to the root or branch classes. Avoiding queries and relationships to the root and branch classes will help to alleviate this burden.  If you must query the root or branch classes there are two methods that JPA providers use, one is to outer join all of the subclass tables, the second is to first query the root table, then query only the required subclass table directly.  The first method has the advantage of only requiring one query, the second has the advantage of avoiding outer joins which typically have poor performance in databases.  You may wish to experiment with each to determine  which mechanism is more efficient in your application and see if your JPA provider supports that mechanism.  Typically the multiple query mechanism is more efficient, but this generally depends on the speed of your database connection.


 * TopLink / EclipseLink : Support both querying mechanisms. The multiple query mechanism is used by default.  Outer joins can be used instead through using a   and the  's  's   method.

Do not have/want a table for every subclass

 * Most inheritance hierarchies do not fit with either the joined or the single table inheritance strategy. Typically the desired strategy is somewhere in between, having joined tables in some subclasses and not in others.  Unfortunately JPA does not directly support this.  One workaround is to map your inheritance hierarchy as single table, but then add the additional tables in the subclasses, either through defining a   or   in each subclass as required.  Depending on your JPA provider, this may work (don't forget to sacrifice the chicken).  If it does not work, then you may need to use a JPA provider specific solution if one exists for your provider, otherwise live within the constraints of having either a single table or one per subclass.  You could also change your inheritance hierarchy so it matches your data model, so if the subclass does not have a table, then collapse its class into its superclass.

No class discriminator column

 * If you are mapping to an existing database schema, your table may not have a class discriminator column. Some JPA providers do not require a class discriminator when using a joined inheritance strategy, so this may be one solution.  Otherwise you need some way to determine the class for a row.  Sometimes the inherited value can be computed from several columns, or there is an discriminator but not a one to one mapping from value to class.  Some JPA providers provide extended support for this.  Another option is to create a database view that manufactures the discriminator column, and then map your hierarchy to this view instead of the table.


 * TopLink / EclipseLink : Support computing the inheritance discriminator through Java code. This can be done through using a   and the  's  's   method.


 * Hibernate : This can be accomplished through using the Hibernate  annotation.  This allows database specific SQL or functions to be used to compute the discriminator value.

=Advanced=

Table Per Class Inheritance
Table per class inheritance allows inheritance to be used in the object model, when it does not exist in the data model. In table per class inheritance a table is defined for each concrete class in the inheritance hierarchy to store all the attributes of that class and all of its superclasses. Be cautious using this strategy as it is optional in the JPA spec, and querying root or branch classes can be very difficult and inefficient.

Example table per class inheritance tables in database
SMALLPROJECT (table)

LARGEPROJECT (table)

Poor query performance

 * The main disadvantage to the table per class model is queries or relationships to the root or branch classes become expensive. Querying the root or branch classes require multiple queries, or unions.  One solution is to use single table inheritance instead, this is good if the classes have a lot in common, but if it is a big hierarchy and the subclasses have little in common this may not be desirable.  Another solution is to remove the table per class inheritance and instead use a , but this means that you can no longer query or have relationships to the class.

Issues with ordering and joins

 * Because table per class inheritance requires multiple queries, or unions, you cannot join to, fetch join, or traverse them in queries. Also when ordering is used the results will be ordered by class, then by the ordering.  These limitations depend on your JPA provider, some JPA provider may have other limitations, or not support table per class at all as it is optional in the JPA spec.

Mapped Superclasses
Mapped superclass inheritance allows inheritance to be used in the object model, when it does not exist in the data model. It is similar to table per class inheritance, but does not allow querying, persisting, or relationships to the superclass. Its main purpose is to allow mappings information to be inherited by its subclasses. The subclasses are responsible for defining the table, id and other information, and can modify any of the inherited mappings. A common usage of a mapped superclass is to define a common  for your application to define common behavior and mappings such as the id and version. A mapped superclass normally should be an abstract class. A mapped superclass is not an  but is instead defined through the   annotation or the   element.

Example mapped superclass tables in database
SMALLPROJECT (table)

LARGEPROJECT (table)

Cannot query, persist, or have relationships

 * The main disadvantage of mapped superclasses is that they cannot be queried or persisted. You also cannot have a relationship to a mapped superclass.  If you require any of these then you must use another inheritance model, such as table per class, which is virtually identical to a mapped superclass except it (may) not have these limitations.  Another alternative is to change your model such that your classes do not have relationships to the superclass, such as changing the relationship to a subclass, or removing the relationship and instead querying for its value by querying each possible subclass and collecting the results in Java.

Subclass does not want to inherit mappings

 * Sometimes you have a subclass that needs to be mapped differently than its parent, or is similar to its' parent but does not have one of the fields, or uses it very differently. Unfortunately it is very difficult not to inherit everything from your parent in JPA, you can override a mapping, but you cannot remove one, or change the type of mapping, or the target class.  If you define your mappings as properties (get methods), or through XML, you may be able to attempt to override or mark the inherited mapping as , this may work depending on your JPA provider (don't forget to sacrifice a chicken).


 * Another solution is to actually fix your inheritance in your object model. If you inherit   from   but don't want to inherit it, then remove it from , if the other subclasses need it, either add it to each, or create a   subclass of   that has the   and have the other subclasses extend this.


 * Some JPA providers may provide ways to be less stringent on inheritance.


 * TopLink / EclipseLink : Allow a subclass remove a mapping, redefine a mapping, or be entirely independent of its superclass. This can be done through using a   and removing the  's mapping, or adding a mapping with the same attribute name, or removing the.

/Mapping