Although relational database systems don’t provide support for inheritance, Hibernate provides several strategies to leverage this object-oriented trait onto domain model entities:
- MappedSuperclass
-
Inheritance is implemented in the domain model only without reflecting it in the database schema. See MappedSuperclass.
- Single table
-
The domain model class hierarchy is materialized into a single table which contains entities belonging to different class types. See Single table.
- Joined table
-
The base class and all the subclasses have their own database tables and fetching a subclass entity requires a join with the parent table as well. See Joined table.
- Table per class
-
Each subclass has its own table containing both the subclass and the base class properties. See Table per class.
In the following domain model class hierarchy, a DebitAccount
and a CreditAccount
share the same Account
base class.
When using MappedSuperclass
, the inheritance is visible in the domain model only, and each database table contains both the base class and the subclass properties.
@MappedSuperclass
inheritancelink:../../../../../test/java/org/hibernate/userguide/inheritance/MappedSuperclassTest.java[role=include]
link:extras/inheritance/entity-inheritance-mapped-superclass-example.sql[role=include]
Note
|
Because the |
The single table inheritance strategy maps all subclasses to only one database table. Each subclass declares its own persistent properties. Version and id properties are assumed to be inherited from the root class.
Note
|
When omitting an explicit inheritance strategy (e.g. |
link:../../../../../test/java/org/hibernate/userguide/inheritance/SingleTableTest.java[role=include]
link:extras/inheritance/entity-inheritance-single-table-example.sql[role=include]
Each subclass in a hierarchy must define a unique discriminator value, which is used to differentiate between rows belonging to separate subclass types.
If this is not specified, the DTYPE
column is used as a discriminator, storing the associated subclass name.
link:../../../../../test/java/org/hibernate/userguide/inheritance/SingleTableTest.java[role=include]
link:extras/inheritance/entity-inheritance-single-table-persist-example.sql[role=include]
When using polymorphic queries, only a single table is required to be scanned to fetch all associated subclass instances.
link:../../../../../test/java/org/hibernate/userguide/inheritance/SingleTableTest.java[role=include]
link:extras/inheritance/entity-inheritance-single-table-query-example.sql[role=include]
Important
|
Among all other inheritance alternatives, the single table strategy performs the best since it requires access to one table only.
Because all subclass columns are stored in a single table, it’s not possible to use NOT NULL constraints anymore, so integrity checks must be moved either into the data access layer or enforced through |
The discriminator column contains marker values that tell the persistence layer what subclass to instantiate for a particular row.
Hibernate Core supports the following restricted set of types as discriminator column: String
, char
, int
, byte
, short
, boolean
(including yes_no
, true_false
).
Use the @DiscriminatorColumn
to define the discriminator column as well as the discriminator type.
Note
|
The enum
The The second option, |
Important
|
There used to be a |
Assuming a legacy database schema where the discriminator is based on inspecting a certain column,
we can take advantage of the Hibernate specific @DiscriminatorFormula
annotation and map the inheritance model as follows:
link:../../../../../test/java/org/hibernate/userguide/inheritance/SingleTableDiscriminatorFormulaTest.java[role=include]
link:extras/inheritance/entity-inheritance-single-table-discriminator-formula-example.sql[role=include]
The @DiscriminatorFormula
defines a custom SQL clause that can be used to identify a certain subclass type.
The @DiscriminatorValue
defines the mapping between the result of the @DiscriminatorFormula
and the inheritance subclass type.
Aside from the usual discriminator values assigned to each individual subclass type, the @DiscriminatorValue
can take two additional values:
null
-
If the underlying discriminator column is null, the
null
discriminator mapping is going to be used. not null
-
If the underlying discriminator column has a not-null value that is not explicitly mapped to any entity, the
not-null
discriminator mapping used.
To understand how these two values work, consider the following entity mapping:
null
and not-null
entity mappinglink:../../../../../test/java/org/hibernate/userguide/inheritance/DiscriminatorNotNullSingleTableTest.java[role=include]
The Account
class has a @DiscriminatorValue( "null" )
mapping, meaning that any account
row which does not contain any discriminator value will be mapped to an Account
base class entity.
The DebitAccount
and CreditAccount
entities use explicit discriminator values.
The OtherAccount
entity is used as a generic account type because it maps any database row whose discriminator column is not explicitly assigned to any other entity in the current inheritance tree.
To visualize how it works, consider the following example:
null
and not-null
entity persistencelink:../../../../../test/java/org/hibernate/userguide/inheritance/DiscriminatorNotNullSingleTableTest.java[role=include]
link:extras/inheritance/entity-inheritance-single-table-discriminator-value-persist-example.sql[role=include]
As you can see, the Account
entity row has a value of NULL
in the DTYPE
discriminator column,
while the OtherAccount
entity was saved with a DTYPE
column value of other
which has not explicit mapping.
Each subclass can also be mapped to its own table. This is also called table-per-subclass mapping strategy. An inherited state is retrieved by joining with the table of the superclass.
A discriminator column is not required for this mapping strategy. Each subclass must, however, declare a table column holding the object identifier.
link:../../../../../test/java/org/hibernate/userguide/inheritance/JoinTableTest.java[role=include]
link:extras/inheritance/entity-inheritance-joined-table-example.sql[role=include]
Note
|
The primary keys of the The table name still defaults to the non-qualified class name.
Also, if |
@PrimaryKeyJoinColumn
link:../../../../../test/java/org/hibernate/userguide/inheritance/JoinTablePrimaryKeyJoinColumnTest.java[role=include]
link:extras/inheritance/entity-inheritance-joined-table-primary-key-join-column-example.sql[role=include]
When using polymorphic queries, the base class table must be joined with all subclass tables to fetch every associated subclass instance.
link:../../../../../test/java/org/hibernate/userguide/inheritance/JoinTableTest.java[role=include]
link:extras/inheritance/entity-inheritance-joined-table-query-example.sql[role=include]
Important
|
The joined table inheritance polymorphic queries can use several JOINS which might affect performance when fetching a large number of entities. |
A third option is to map only the concrete classes of an inheritance hierarchy to tables. This is called the table-per-concrete-class strategy. Each table defines all persistent states of the class, including the inherited state.
In Hibernate, it is not necessary to explicitly map such inheritance hierarchies. You can map each class as a separate entity root. However, if you wish use polymorphic associations (e.g. an association to the superclass of your hierarchy), you need to use the union subclass mapping.
link:../../../../../test/java/org/hibernate/userguide/inheritance/TablePerClassTest.java[role=include]
link:extras/inheritance/entity-inheritance-table-per-class-example.sql[role=include]
When using polymorphic queries, a UNION is required to fetch the base class table along with all subclass tables as well.
link:../../../../../test/java/org/hibernate/userguide/inheritance/TablePerClassTest.java[role=include]
link:extras/inheritance/entity-inheritance-table-per-class-query-example.sql[role=include]
Important
|
Polymorphic queries require multiple UNION queries, so be aware of the performance implications of a large class hierarchy. Unfortunately, not all database systems support UNION ALL, in which case, UNION is going to be used instead of UNION ALL. The following Hibernate dialects support UNION ALL:
|
By default, when you query a base class entity, the polymorphic query will fetch all subclasses belonging to the base type.
However, you can even query interfaces or base classes that don’t belong to the JPA entity inheritance model.
For instance, considering the following DomainModelEntity
interface:
link:../../../../../test/java/org/hibernate/userguide/inheritance/polymorphism/DomainModelEntity.java[role=include]
If we have two entity mappings, a Book
and a Blog
,
and the Blog
entity is mapped with the
@Polymorphism
annotation
and taking the PolymorphismType.EXPLICIT
setting:
@Polymorphism
entity mappinglink:../../../../../test/java/org/hibernate/userguide/inheritance/polymorphism/ExplicitPolymorphismTest.java[role=include]
If we have the following entity objects in our system:
link:../../../../../test/java/org/hibernate/userguide/inheritance/polymorphism/ExplicitPolymorphismTest.java[role=include]
We can now query against the DomainModelEntity
interface,
and Hibernate is going to fetch only the entities that are either mapped with
@Polymorphism(type = PolymorphismType.IMPLICIT)
or they are not annotated at all with the @Polymorphism
annotation (implying the IMPLICIT behavior):
link:../../../../../test/java/org/hibernate/userguide/inheritance/polymorphism/ExplicitPolymorphismTest.java[role=include]
Therefore, only the Book
was fetched since the Blog
entity was marked with the
@Polymorphism(type = PolymorphismType.EXPLICIT)
annotation, which instructs Hibernate
to skip it when executing a polymorphic query against a non-mapped base class.