How do you implement multi-table inheritance in JPA?
Table of Contents
- Introduction
- Inheritance Strategies in JPA
- Conclusion
Introduction
In JPA (Java Persistence API), inheritance allows you to model relationships between entity classes that share common attributes. When you have a class hierarchy where subclasses share some common fields and behaviors but also have their own distinct attributes, you can use multi-table inheritance strategies to map these relationships to database tables.
In JPA, multi-table inheritance means that the fields of a superclass and its subclasses are mapped across multiple tables in the database. This is in contrast to a single-table inheritance approach, where all fields from the superclass and subclasses are stored in one table.
JPA provides three main inheritance strategies to manage multi-table inheritance:
- Single Table Inheritance (
**SINGLE_TABLE**
): All entities are mapped to a single table, with a discriminator column to differentiate between the types. - Joined Table Inheritance (
**JOINED**
): Each entity in the hierarchy is mapped to its own table, with a shared base table that stores the common attributes. - Table Per Class Inheritance (
**TABLE_PER_CLASS**
): Each entity in the hierarchy is mapped to its own table, without a shared base table.
In this article, we'll look at how to implement multi-table inheritance in JPA using these strategies.
Inheritance Strategies in JPA
1. Single Table Inheritance (**SINGLE_TABLE**
)
In single-table inheritance, all entities in the hierarchy are stored in a single table. The table will have a discriminator column that determines which subclass an entity belongs to. This strategy is efficient in terms of database queries but may result in sparse tables if subclasses have many unique fields.
Example: Single Table Inheritance
Explanation:
- Discriminator Column: The
@DiscriminatorColumn
annotation specifies the column used to distinguish between different entity types (e.g.,entity_type
). - Discriminator Value: The
@DiscriminatorValue
annotation is used to specify the value for each subclass in theentity_type
column. In this example,Dog
andCat
will have different discriminator values.
Database Schema:
2. Joined Table Inheritance (**JOINED**
)
In joined-table inheritance, each class in the hierarchy (including the superclass and subclasses) is mapped to a separate table. The common attributes of all entities are stored in a base table, and each subclass has its own table. The tables are joined using the primary key of the superclass.
Example: Joined Table Inheritance
@Entity public class Dog extends Animal { private boolean isVaccinated; // Getters and setters }
Explanation:
- Inheritance Type: The
@Inheritance(strategy = InheritanceType.JOINED)
annotation specifies that theJOINED
inheritance strategy should be used. - Table Structure:
- The
Animal
class will be mapped to one table. - The
Dog
andCat
classes will be mapped to separate tables with a foreign key to theAnimal
table.
- The
Database Schema:
3. Table Per Class Inheritance (**TABLE_PER_CLASS**
)
In the table-per-class inheritance strategy, each class in the inheritance hierarchy is mapped to a separate table, but unlike the JOINED
strategy, there is no base table. Each subclass has its own table, and there is no shared primary key between tables. This means that data redundancy can occur, but queries can be more efficient for subclasses since there’s no need for joins.
Example: Table Per Class Inheritance
Explanation:
- Inheritance Type: The
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
annotation specifies theTABLE_PER_CLASS
inheritance strategy. - Table Structure:
- Each subclass (
Dog
andCat
) has its own table, and there is no base table for the common attributes. - There is data duplication because each table contains its own copy of the fields from the parent class.
- Each subclass (
Database Schema:
Comparison of Inheritance Strategies
Feature | SINGLE_TABLE | JOINED | TABLE_PER_CLASS |
---|---|---|---|
Number of Tables | 1 (all entities in a single table) | 3 (base table + 1 table for each subclass) | 2 or more (1 table per subclass) |
Efficiency | Fast for reads (no joins required) | Slower due to join operations | Slower for reads (no joins, but more tables) |
Data Redundancy | Less redundancy | No redundancy, shared attributes in base table | More redundancy (each subclass has its own table) |
Flexibility | Less flexible, harder to extend | More flexible, better for complex hierarchies | Can be inefficient due to duplication of columns |
Use Case | Best for simpler hierarchies | Best for complex hierarchies with shared attributes | Best for independent entities with little shared data |
Conclusion
In JPA, implementing multi-table inheritance can be done using the SINGLE_TABLE
, JOINED
, and TABLE_PER_CLASS
inheritance strategies. Each strategy has its own use cases, trade-offs, and performance implications:
- Single Table Inheritance is best suited for simple hierarchies and provides the best performance for querying all entities in the hierarchy.
- Joined Table Inheritance is ideal when you want to normalize the database schema and avoid data redundancy, at the cost of additional joins in queries.
- Table Per Class Inheritance is useful when you want each entity to have its own table, but it can lead to data duplication and inefficiency in complex hierarchies.
By choosing the appropriate inheritance strategy for your application, you can ensure that your database schema aligns with your domain model and provides efficient persistence for your entities.