How do you implement inheritance in JPA?
Table of Contents
Introduction
In Java Persistence API (JPA), inheritance is an essential feature that allows you to model object-oriented class hierarchies in relational databases. JPA provides several strategies for mapping the inheritance relationships of Java classes to database tables, making it easier to work with polymorphic objects and manage complex data models.
The @Inheritance
annotation in JPA is used to specify the inheritance strategy, determining how subclass entities are stored and retrieved from the database. Depending on the chosen strategy, JPA can map the inheritance hierarchy to one or multiple tables.
In this guide, we'll cover the different inheritance strategies in JPA and demonstrate how to implement inheritance in JPA entities.
Inheritance Strategies in JPA
JPA supports three main inheritance strategies:
- Single Table Inheritance (SINGLE_TABLE)
- Joined Table Inheritance (JOINED)
- Table Per Class Inheritance (TABLE_PER_CLASS)
Each strategy has its own advantages and trade-offs, depending on your application's needs.
1. Single Table Inheritance (SINGLE_TABLE)
In Single Table Inheritance, all classes in the inheritance hierarchy are mapped to a single database table. A special discriminator column is used to differentiate between the different types of entities stored in the same table.
How it works:
- One table holds the fields of all entities in the hierarchy.
- A discriminator column is added to identify the specific subclass type of each row.
- This strategy is simple and efficient but can lead to sparse tables if the subclasses have significantly different fields.
Example:
In the example above:
**Vehicle**
is the abstract superclass, and both**Car**
and**Bike**
are concrete subclasses.- The
**@Inheritance**
annotation specifies that SINGLE_TABLE strategy will be used. - The
**@DiscriminatorColumn**
annotation defines a column (type
) to distinguish betweenCar
andBike
. **@DiscriminatorValue**
is used in each subclass to specify the value of the discriminator column for that type.
The resulting table might look like this:
id | model | type | doors | hasPedals |
---|---|---|---|---|
1 | Sedan | CAR | 4 | NULL |
2 | MTB | BIKE | NULL | true |
2. Joined Table Inheritance (JOINED)
In Joined Table Inheritance, each class in the inheritance hierarchy is mapped to its own table. The table of each subclass contains a foreign key to its parent class table, ensuring that the relationship between the entities is preserved.
How it works:
- A separate table is created for each class in the hierarchy.
- Subclass tables store only the fields that are specific to the subclass, and a foreign key links the subclass table to the parent table.
- This strategy is more normalized but can result in more complex joins and potentially worse performance.
Example:
In this example:
- Each entity (
Vehicle
,Car
,Bike
) is stored in its own table. - The
**@Inheritance**
annotation specifies the JOINED strategy. - The parent table (
Vehicle
) contains common fields (likeid
andmodel
), while the subclass tables (Car
andBike
) contain fields specific to those classes (likedoors
andhasPedals
). - The foreign key in
Car
andBike
tables points to theVehicle
table.
The resulting tables might look like this:
Vehicle Table:
id | model |
---|---|
1 | Sedan |
2 | MTB |
Car Table:
id | doors |
---|---|
1 | 4 |
Bike Table:
id | hasPedals |
---|---|
2 | true |
3. Table Per Class Inheritance (TABLE_PER_CLASS)
In Table Per Class Inheritance, each class in the hierarchy is mapped to its own table, and there is no foreign key relationship between the tables. All columns from the parent class are duplicated in the subclass tables, leading to less normalized data.
How it works:
- Each class has its own table, and each table includes columns for the fields in the entire hierarchy (both parent and subclass fields).
- This strategy avoids joins but can result in data redundancy.
Example:
In this example:
- The
Vehicle
,Car
, andBike
classes are mapped to separate tables. - Each subclass table contains the fields from both the parent class (
Vehicle
) and the subclass itself (Car
orBike
).
The resulting tables might look like this:
Vehicle Table:
id | model |
---|---|
1 | Sedan |
2 | MTB |
Car Table:
id | model | doors |
---|---|---|
1 | Sedan | 4 |
Bike Table:
id | model | hasPedals |
---|---|---|
2 | MTB | true |
Conclusion
In JPA, inheritance can be mapped to the database using various strategies provided by the @Inheritance
annotation. The three primary strategies are:
- Single Table (SINGLE_TABLE): Stores all entities in the hierarchy in one table, with a discriminator column to distinguish the subclasses.
- Joined (JOINED): Each class in the hierarchy is mapped to a separate table, with foreign keys linking subclasses to the parent class.
- Table Per Class (TABLE_PER_CLASS): Each class in the hierarchy has its own table, with no foreign keys or shared columns.
- Each strategy has its advantages and trade-offs in terms of normalization, performance, and complexity, so choosing the right strategy depends on the specific needs of your application.