4. Inheritance Mappings

1. @Inheritance

About

  • @Inheritance is an annotation used in JPA to define inheritance mapping strategies between entity classes.

  • It specifies how the inheritance between parent and child entity classes should be mapped into relational database tables.

Why Is It Needed?

  • In Java, it's natural to use class inheritance for code reuse and modeling hierarchy.

  • In relational databases, there is no native inheritance — every table is flat.

  • So JPA must simulate inheritance by deciding how to map the class hierarchy to tables.

Where @Inheritance is Used ?

When we have:

  • A base entity class (abstract or concrete).

  • Multiple subclasses extending the base.

  • We want the inheritance relationship persisted properly in DB.

Syntax of @Inheritance

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Parent {
    // fields
}
  • strategy is mandatory. It tells JPA which way to map the hierarchy.

Inheritance Strategies in JPA

There are 3 official strategies under @Inheritance(strategy = ...):

Strategy
Meaning

SINGLE_TABLE

One table for all classes.

TABLE_PER_CLASS

One table per concrete class.

JOINED

Multiple tables linked by primary keys.

1. InheritanceType.SINGLE_TABLE

  • All entities in the hierarchy are mapped into a single table.

  • One big table with columns from parent and child classes.

  • Discriminator column is used to differentiate which row belongs to which class.

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "type")
public class Employee {
    @Id
    private Long id;
    private String name;
}
@Entity
@DiscriminatorValue("manager")
public class Manager extends Employee {
    private String departmentName;
}
@Entity
@DiscriminatorValue("developer")
public class Developer extends Employee {
    private String programmingLanguage;
}

Database Table

id
name
departmentName
programmingLanguage
type

1

John

HR

NULL

manager

2

Jane

NULL

Java

developer

Characteristics

Aspect
Details

Performance

Fastest (only one table to join).

Schema

Sparse columns (many NULLs if many subclasses).

Flexibility

Hard to add constraints because unrelated fields exist in same table.

Discriminator

Mandatory (@DiscriminatorColumn).

Example use case

Small inheritance hierarchies with few fields.

2. InheritanceType.TABLE_PER_CLASS

  • Each concrete entity gets its own table.

  • Each table duplicates the columns inherited from parent.

  • No need for discriminator column.

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Employee {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
}
@Entity
public class Manager extends Employee {
    private String departmentName;
}
@Entity
public class Developer extends Employee {
    private String programmingLanguage;
}

Database Tables

Separate table for each subclass. Each table will have own copy of inherited fields.

Manager Table

id
name
departmentName

1

John

HR

3

Bob

Finance

Developer Table

id
name
programmingLanguage

2

Jane

Java

Notes:

  • No NULLs.

  • Duplicate columns (id, name) in all tables.

  • Querying across all employees needs a UNION.

Characteristics

Aspect
Details

Performance

Inserts fast. Queries across hierarchy are slow (UNION needed).

Schema

No NULL fields.

Flexibility

Good if subclasses are quite different.

Discriminator

Not needed.

Example use case

When subclasses are very different and rarely queried together.

3. InheritanceType.JOINED

  • Parent and child classes have their own tables.

  • Tables are linked via primary key / foreign key.

  • Fetching a subclass involves a JOIN.

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public class Employee {
    @Id
    private Long id;
    private String name;
}
@Entity
public class Manager extends Employee {
    private String departmentName;
}
@Entity
public class Developer extends Employee {
    private String programmingLanguage;
}

Database Tables

One table for base class and one for each subclass. Tables are linked by primary key = foreign key.

Employee Table (base table)

id
name

1

John

2

Jane

3

Bob

Manager Table

id
departmentName

1

HR

3

Finance

Developer Table

id
programmingLanguage

2

Java

Notes:

  • id is Primary Key and Foreign Key in child tables.

  • Fetching a full Manager/Developer record requires JOIN on id.

  • Very normalized.

Characteristics

Aspect
Details

Performance

Slower (needs JOINs on select).

Schema

Normalized (no NULLs).

Flexibility

Best for strict relational modeling.

Discriminator

Optional (can use).

Example use case

When normalized design is required, and JOIN cost is acceptable.

2. @DiscriminatorColumn

About

@DiscriminatorColumn is a JPA annotation used in the context of inheritance mapping in JPA to define a discriminator column. It helps in distinguishing between different entities in the same table when the SINGLE_TABLE or JOINED inheritance strategy is used. This column holds the value that determines which subclass the current row corresponds to.

Why is it Needed?

  • Inheritance Mapping: When using single-table inheritance, all subclasses are stored in the same table. The discriminator column is used to store the type of entity (i.e., the subclass) for each row.

  • Class Differentiation: In databases, there's no direct concept of inheritance, so JPA uses the discriminator column to mark which entity a row belongs to.

  • Performance: The discriminator column avoids using complex joins and makes querying easier and more efficient in the SINGLE_TABLE strategy.

Where is @DiscriminatorColumn Used ?

  • It is used in the parent entity class where inheritance is being mapped.

  • It works in the context of SINGLE_TABLE or JOINED inheritance strategies, where a single table stores all the child entity data (for SINGLE_TABLE) or child entities have their own table but still rely on the base entity table (for JOINED).

Syntax

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "employee_type", discriminatorType = DiscriminatorType.STRING)
public class Employee {
    @Id
    private Long id;
    private String name;
}
  • name: Specifies the name of the discriminator column (e.g., employee_type in the example).

  • discriminatorType: Specifies the type of the discriminator column (STRING, INTEGER, CHAR, etc.). Default is DiscriminatorType.STRING.

How It Works ?

  • The @DiscriminatorColumn defines a special column in the table that stores a discriminator value.

  • The value in this column helps JPA determine which class the row belongs to.

  • @DiscriminatorValue on each subclass specifies the value that will be inserted in the discriminator column for that subclass.

Example Scenario with SINGLE_TABLE Inheritance:

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "employee_type", discriminatorType = DiscriminatorType.STRING)
public class Employee {
    @Id
    private Long id;
    private String name;
}

@Entity
@DiscriminatorValue("MANAGER")
public class Manager extends Employee {
    private String departmentName;
}

@Entity
@DiscriminatorValue("DEVELOPER")
public class Developer extends Employee {
    private String programmingLanguage;
}

Database Table Structure (for SINGLE_TABLE strategy):

id
name
employee_type
departmentName
programmingLanguage

1

John

MANAGER

HR

NULL

2

Alice

DEVELOPER

NULL

Java

3

Bob

DEVELOPER

NULL

Python

Key Characteristics

Feature
Details

Purpose

To distinguish between entities when mapped into a single table (or joined strategy).

Discriminator Value

The value in this column helps identify which subclass the row belongs to.

Column Type

Can be STRING, CHAR, INTEGER, etc., depending on the data.

Use Case

Essential for the SINGLE_TABLE inheritance strategy.

Defaults

If not defined, defaults to DTYPE as the column name, and DiscriminatorType.STRING for type.

Inheritance Type

Primarily used in SINGLE_TABLE inheritance, but can also be used in JOINED.

Customizing @DiscriminatorColumn

We can further customize the discriminator column with additional attributes:

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(
    name = "employee_type",
    discriminatorType = DiscriminatorType.STRING,
    length = 50, // Set the maximum length of the discriminator value
    columnDefinition = "VARCHAR(50) NOT NULL" // Custom SQL definition for the column
)
public class Employee {
    @Id
    private Long id;
    private String name;
}

Attributes

  • name: Name of the discriminator column.

  • discriminatorType: Type of the discriminator value (can be STRING, INTEGER, etc.).

  • length: Optional. Specifies the maximum length for the discriminator column (default is 31).

  • columnDefinition: Optional. Allows for specifying the column's SQL definition.

When Not to Use @DiscriminatorColumn

  • Performance Concerns: The SINGLE_TABLE strategy may become inefficient when there are a lot of fields that are not relevant for certain subclasses, leading to sparse tables with many NULL values. In such cases, other strategies like JOINED or TABLE_PER_CLASS may be more efficient.

  • Complex Inheritance: If the hierarchy has many deep or unrelated subclasses, managing the @DiscriminatorColumn can become difficult, and you might prefer using a different inheritance strategy.

Best Practices

Best Practice
Details

Keep it Simple

Use SINGLE_TABLE and @DiscriminatorColumn for simple hierarchies. Avoid deep inheritance trees.

Column Type

Use DiscriminatorType.STRING for most cases, but if you need a number, use DiscriminatorType.INTEGER or CHAR for efficiency.

Avoid NULLs

If you are using SINGLE_TABLE strategy, consider the schema design and avoid having many NULL columns for subclasses that don't need them.

Customization

Customize the column definition using columnDefinition if you need more control over how the column is created in the database.

Clear Naming

Ensure clear and meaningful values for the discriminator column values using @DiscriminatorValue.

3. @DiscriminatorValue

About

@DiscriminatorValue is a JPA annotation used to specify the value that will be stored in the discriminator column for each subclass of a parent entity when inheritance is mapped using SINGLE_TABLE or JOINED inheritance strategies. The discriminator column (defined by @DiscriminatorColumn) helps JPA differentiate between different subclasses of the same parent entity in the database.

Why is @DiscriminatorValue Needed?

  • Inheritance Strategy: It is crucial for the SINGLE_TABLE inheritance strategy where all subclasses are stored in the same table. Each subclass needs a discriminator value to tell which entity type the row corresponds to.

  • Entity Type Identification: Without @DiscriminatorValue, JPA cannot distinguish between different types of entities stored in the same table.

  • Efficient Data Storage: Helps in storing and querying different entity types in the same table, which can improve performance and reduce the need for multiple database tables in certain situations.

Where is @DiscriminatorValue Used?

  • It is applied on subclass entities that inherit from a parent entity using a single table inheritance strategy (SINGLE_TABLE or JOINED).

  • Each subclass must declare a value for @DiscriminatorValue that is inserted into the discriminator column when an instance of the subclass is persisted.

Syntax of @DiscriminatorValue

@Entity
@DiscriminatorValue("SUBCLASS_NAME")
public class Subclass extends ParentClass {
    // Class implementation
}
  • @DiscriminatorValue("SUBCLASS_NAME"): Specifies the value that will be stored in the discriminator column for this subclass.

How It Works ?

  • The @DiscriminatorValue annotation is applied to subclasses in the inheritance hierarchy.

  • When an instance of the subclass is saved to the database, JPA inserts the corresponding discriminator value into the discriminator column (defined by @DiscriminatorColumn in the parent entity).

  • JPA uses the value stored in the discriminator column to identify which subclass the row belongs to when querying the data.

Example

Consider a scenario where we have an Employee superclass and two subclasses: Manager and Developer.

Parent Entity

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "employee_type", discriminatorType = DiscriminatorType.STRING)
public class Employee {
    @Id
    private Long id;
    private String name;
    // Getter and Setter methods
}

Subclass 1: Manager

@Entity
@DiscriminatorValue("MANAGER")
public class Manager extends Employee {
    private String departmentName;
    // Getter and Setter methods
}

Subclass 2: Developer

@Entity
@DiscriminatorValue("DEVELOPER")
public class Developer extends Employee {
    private String programmingLanguage;
    // Getter and Setter methods
}

Database Table Structure (for SINGLE_TABLE strategy)

id
name
employee_type
departmentName
programmingLanguage

1

John

MANAGER

HR

NULL

2

Alice

DEVELOPER

NULL

Java

3

Bob

DEVELOPER

NULL

Python

In this case:

  • The employee_type column is the discriminator column.

  • The value "MANAGER" is inserted for the Manager subclass, and "DEVELOPER" for the Developer subclass.

Characteristics of @DiscriminatorValue

Feature
Details

Purpose

Specifies the value that will be stored in the discriminator column for a specific subclass.

Target

Applied to subclasses in an inheritance hierarchy.

Inheritance Strategy

Primarily used in the SINGLE_TABLE inheritance strategy. It can also be used in JOINED.

Discriminator Column

The value is inserted into the column defined by @DiscriminatorColumn.

Value Type

The value can be a string, integer, or other simple types, based on the discriminator column type.

Role

Helps JPA identify which subclass the row in the table represents.

Required

Yes, each subclass in a SINGLE_TABLE inheritance strategy must declare a discriminator value.

Default Behavior of @DiscriminatorValue

If @DiscriminatorValue is not specified on a subclass, JPA will use the class name (converted to a string) as the discriminator value by default.

Example (Implicit Default):

@Entity
@DiscriminatorValue("MANAGER")
public class Manager extends Employee {
    private String departmentName;
}

If @DiscriminatorValue were omitted, the discriminator value for Manager would be automatically set to "Manager", which is the name of the class.

Customizing @DiscriminatorValue

We can customize the discriminator values to make them more readable and relevant to the domain model.

@Entity
@DiscriminatorValue("HR_MANAGER")
public class HRManager extends Manager {
    private String region;
    // Getter and Setter methods
}

In this case, the value "HR_MANAGER" will be used for rows representing HRManager in the discriminator column.

When Not to Use @DiscriminatorValue

  • Complex Relationships: When the inheritance hierarchy involves complex relationships, consider using the JOINED strategy instead, which splits each class into its own table.

  • Multiple Inheritance: If the entity hierarchy is deep with many unrelated subclasses, the SINGLE_TABLE strategy might become inefficient because of the large number of NULL values in the columns.

Last updated