How do you configure inheritance strategies in JPA?
Table of Contents
Introduction
Inheritance in Java is a fundamental concept that allows one class (the subclass) to inherit attributes and methods from another class (the superclass). In Java Persistence API (JPA), inheritance strategies define how to map Java class hierarchies to relational database tables. JPA provides several strategies for inheritance, and configuring them correctly is essential for effective object-relational mapping (ORM).
The @Inheritance annotation in JPA allows you to configure inheritance mapping for your entity classes. This annotation specifies how the subclass entities (derived classes) should be stored in the database in relation to their parent (superclass) entities. Depending on your application's requirements, you can choose between three primary inheritance strategies: SINGLE_TABLE, JOINED, and TABLE_PER_CLASS.
In this guide, we'll walk through how to configure each inheritance strategy in JPA.
Configuring Inheritance Strategies in JPA
1. SINGLE_TABLE Strategy
The SINGLE_TABLE inheritance strategy maps all entities in the hierarchy (both the superclass and subclasses) to a single table. A discriminator column is used to differentiate between the various entity types. This is the most performance-efficient strategy for reading entities, but it may lead to sparse tables if the subclasses have significantly different attributes.
Configuration:
To configure the SINGLE_TABLE strategy in JPA, use the @Inheritance annotation with strategy = InheritanceType.SINGLE_TABLE. You should also define a discriminator column using the @DiscriminatorColumn annotation, which identifies which subclass each row in the table corresponds to.
Example:
In the example above:
Vehicleis the superclass.CarandBikeare subclasses.- The
@DiscriminatorColumnannotation creates a column (vehicle_type) to distinguish betweenCarandBikeentities.
Table Example:
| id | model | vehicle_type | doors | hasPedals |
|---|---|---|---|---|
| 1 | Sedan | CAR | 4 | NULL |
| 2 | MTB | BIKE | NULL | true |
2. JOINED Strategy
The JOINED strategy creates a separate table for each class in the inheritance hierarchy. The subclass tables contain only the fields specific to that class, while the superclass table holds the common attributes. Subclasses have a foreign key that references the primary key of the superclass. This strategy provides better normalization but may introduce additional overhead due to the need for joins when querying data.
Configuration:
To configure the JOINED strategy, use the @Inheritance annotation with strategy = InheritanceType.JOINED.
Example:
In this configuration:
- The
Vehicletable stores common fields likeidandmodel. - The
CarandBiketables store only their respective fields, with each having a foreign key to theVehicletable.
Table Examples:
Vehicle Table:
| id | model |
|---|---|
| 1 | Sedan |
| 2 | MTB |
Car Table:
| id | doors |
|---|---|
| 1 | 4 |
Bike Table:
| id | hasPedals |
|---|---|
| 2 | true |
3. TABLE_PER_CLASS Strategy
The TABLE_PER_CLASS strategy maps each class in the hierarchy to its own table. The table for each class includes both the inherited and subclass-specific attributes. This strategy does not use joins but can lead to data duplication if the subclass tables have common fields. It is typically used when the subclasses are completely independent and don't share much behavior.
Configuration:
To configure the TABLE_PER_CLASS strategy, use the @Inheritance annotation with strategy = InheritanceType.TABLE_PER_CLASS.
Example:
In this configuration:
- The
Vehicletable is not explicitly required. Each subclass (CarandBike) has its own table with both its own and the inherited fields.
Table Examples:
Vehicle Table (Not Required): This table doesn't exist because each subclass has its own table.
Car Table:
| id | model | doors |
|---|---|---|
| 1 | Sedan | 4 |
Bike Table:
| id | model | hasPedals |
|---|---|---|
| 2 | MTB | true |
Conclusion
Configuring inheritance strategies in JPA is essential for mapping class hierarchies to relational database tables in a way that matches your application's data modeling needs. Each inheritance strategy has its own trade-offs in terms of performance, normalization, and ease of querying:
- SINGLE_TABLE is efficient for querying but may lead to sparse tables.
- JOINED provides normalized data at the cost of more complex queries due to joins.
- TABLE_PER_CLASS avoids joins but may lead to data duplication and is less efficient in some scenarios.
By using the @Inheritance annotation with the appropriate strategy, you can choose the best approach for your application's specific requirements.