Skip to content

Latest commit

 

History

History
393 lines (312 loc) · 15.2 KB

File metadata and controls

393 lines (312 loc) · 15.2 KB

Inheritance

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.

MappedSuperclass

In the following domain model class hierarchy, a DebitAccount and a CreditAccount share the same Account base class.

Inheritance class diagram

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.

Example 1. @MappedSuperclass inheritance
link:../../../../../test/java/org/hibernate/userguide/inheritance/MappedSuperclassTest.java[role=include]
link:extras/inheritance/entity-inheritance-mapped-superclass-example.sql[role=include]
Note

Because the @MappedSuperclass inheritance model is not mirrored at the database level, it’s not possible to use polymorphic queries referencing the @MappedSuperclass when fetching persistent objects by their base class.

Single table

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. @Inheritance), JPA will choose the SINGLE_TABLE strategy by default.

Example 2. Single Table inheritance
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.

Example 3. Single Table inheritance discriminator column
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.

Example 4. Single Table polymorphic query
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 CHECK or TRIGGER constraints.

Discriminator

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 DiscriminatorType used in javax.persistence.DiscriminatorColumn only contains the values STRING, CHAR and INTEGER which means that not all Hibernate supported types are available via the @DiscriminatorColumn annotation. You can also use @DiscriminatorFormula to express in SQL a virtual discriminator column. This is particularly useful when the discriminator value can be extracted from one or more columns of the table. Both @DiscriminatorColumn and @DiscriminatorFormula are to be set on the root entity (once per persisted hierarchy).

@org.hibernate.annotations.DiscriminatorOptions allows to optionally specify Hibernate-specific discriminator options which are not standardized in JPA. The available options are force and insert.

The force attribute is useful if the table contains rows with extra discriminator values that are not mapped to a persistent class. This could, for example, occur when working with a legacy database. If force is set to true, Hibernate will specify the allowed discriminator values in the SELECT query even when retrieving all instances of the root class.

The second option, insert, tells Hibernate whether or not to include the discriminator column in SQL INSERTs. Usually, the column should be part of the INSERT statement, but if your discriminator column is also part of a mapped composite identifier you have to set this option to false.

Important

There used to be a @org.hibernate.annotations.ForceDiscriminator annotation which was deprecated in version 3.6 and later removed. Use @DiscriminatorOptions instead.

Discriminator formula

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:

Example 5. Single Table discriminator formula
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.

Implicit discriminator values

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:

Example 6. @DiscriminatorValue null and not-null entity mapping
link:../../../../../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:

Example 7. @DiscriminatorValue null and not-null entity persistence
link:../../../../../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.

Joined table

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.

Example 8. Join Table
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 CreditAccount and DebitAccount tables are also foreign keys to the superclass table primary key and described by the @PrimaryKeyJoinColumns.

The table name still defaults to the non-qualified class name. Also, if @PrimaryKeyJoinColumn is not set, the primary key / foreign key columns are assumed to have the same names as the primary key columns of the primary table of the superclass.

Example 9. Join Table with @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.

Example 10. Join Table polymorphic query
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.

Table per class

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.

Example 11. Table per class
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.

Example 12. Table per class polymorphic query
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:

  • AbstractHANADialect

  • AbstractTransactSQLDialect

  • CUBRIDDialect

  • DB2Dialect

  • H2Dialect

  • HSQLDialect

  • Ingres9Dialect

  • MySQL5Dialect

  • Oracle8iDialect

  • Oracle9Dialect

  • PostgreSQL81Dialect

  • RDMSOS2200Dialect

Implicit and explicit polymorphism

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:

Example 13. Domain Model Entity 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:

Example 14. @Polymorphism entity mapping
link:../../../../../test/java/org/hibernate/userguide/inheritance/polymorphism/ExplicitPolymorphismTest.java[role=include]

If we have the following entity objects in our system:

Example 15. Domain Model entity objects
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):

Example 16. Fetching Domain Model entities using non-mapped base class polymorphism
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.