Hibernate Inheritance Mapping

In this tutorial, we will discuss how JPA/Hibernate supports Inheritance mapping. We also look into several inheritance strategies that the JPA specification provides.

Relational databases don’t have a straightforward way to map class hierarchies onto database tables.
To address this, the JPA specification provides several strategies:
  1. MappedSuperclass - Inheritance is implemented in the domain model only without reflecting it in the database schema. In this strategy, the parent classes can’t be entities.
  2. Single table - The domain model class hierarchy is materialized into a single table that contains entities belonging to different class types.
  3. 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.
  4. Table per class - Each subclass has its own table containing both the subclass and the base class properties.
Entity inheritance means that we can use polymorphic queries for retrieving all the sub-class entities when querying for a super-class.
Since Hibernate is a JPA implementation, it contains all of the above as well as a few Hibernate-specific features related to inheritance.

Let's discuss each JPA specification inheritance strategy with examples.

1. Hibernate/JPA - MappedSuperclass Inheritance

Example: @MappedSuperclass inheritance

In the following domain model class hierarchy, a DebitAccount and a CreditAccount share the same Account base class.
@MappedSuperclass
public static class Account {

    @Id
    private Long id;

    private String owner;

    private BigDecimal balance;

    private BigDecimal interestRate;

    //Getters and setters are omitted for brevity

}

@Entity(name = "DebitAccount")
public static class DebitAccount extends Account {

    private BigDecimal overdraftFee;

    //Getters and setters are omitted for brevity

}

@Entity(name = "CreditAccount")
public static class CreditAccount extends Account {

    private BigDecimal creditLimit;

    //Getters and setters are omitted for brevity

}
CREATE TABLE DebitAccount (
    id BIGINT NOT NULL ,
    balance NUMERIC(19, 2) ,
    interestRate NUMERIC(19, 2) ,
    owner VARCHAR(255) ,
    overdraftFee NUMERIC(19, 2) ,
    PRIMARY KEY ( id )
)

CREATE TABLE CreditAccount (
    id BIGINT NOT NULL ,
    balance NUMERIC(19, 2) ,
    interestRate NUMERIC(19, 2) ,
    owner VARCHAR(255) ,
    creditLimit NUMERIC(19, 2) ,
    PRIMARY KEY ( id )
)
Complete example at Hibernate/JPA MappedSuperclass Inheritance Example

2. Hibernate/JPA Single Table Inheritance

The single table strategy maps all entities of the inheritance structure to the same database table. This approach makes polymorphic queries very efficient and provides the best performance.
Note that when no explicit inheritance strategy is registered, Hibernate/JPA will choose the SINGLE_TABLE inheritance strategy by default.
SINGLE_TABLE inheritance performs the best in terms of executed SQL statements. However, you cannot use NOT NULL constraints on the column level. You can still use triggers and rules to enforce such constraints, but it’s not as straightforward.

Hibernate/JPA Single Table Inheritance Example

Account.java

package net.javaguides.hibernate.entity;

import jakarta.persistence.*;

@Entity(name = "Account")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class Account {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String owner;

    private double balance;

    private double interestRate;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getOwner() {
        return owner;
    }

    public void setOwner(String owner) {
        this.owner = owner;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    public double getInterestRate() {
        return interestRate;
    }

    public void setInterestRate(double interestRate) {
        this.interestRate = interestRate;
    }
}

CreditAccount.java

package net.javaguides.hibernate.entity;

import jakarta.persistence.Entity;

@Entity(name = "CreditAccount")
public class CreditAccount extends Account {

    private double creditLimit;

    public double getCreditLimit() {
        return creditLimit;
    }

    public void setCreditLimit(double creditLimit) {
        this.creditLimit = creditLimit;
    }
}

DebitAccount.java

package net.javaguides.hibernate.entity;

import jakarta.persistence.Entity;

@Entity(name = "DebitAccount")
public class DebitAccount extends Account {

    private double overdraftFee;

    public double getOverdraftFee() {
        return overdraftFee;
    }

    public void setOverdraftFee(double overdraftFee) {
        this.overdraftFee = overdraftFee;
    }
}
Complete example at Hibernate/JPA Single Table Inheritance Example

3. Hibernate/JPA Joined Table Inheritance 

Each subclass can also be mapped to its own table. This is also called the table-per-subclass mapping strategy. An inherited state is retrieved by joining 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.
The JOINED table inheritance strategy addresses the data integrity concerns because every subclass is associated with a different table. Polymorphic queries or @OneToMany base class associations don’t perform very well with this strategy. However, polymorphic @ManyToOne associations are fine, and they can provide a lot of value.

Joined Table Inheritance Example

Let's use the @Inheritance(strategy = InheritanceType.JOINED) annotation to use this strategy.

Account.java

package net.javaguides.hibernate.entity;

import jakarta.persistence.*;

@Entity(name = "Account")
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class Account {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String owner;

    private double balance;

    private double interestRate;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getOwner() {
        return owner;
    }

    public void setOwner(String owner) {
        this.owner = owner;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    public double getInterestRate() {
        return interestRate;
    }

    public void setInterestRate(double interestRate) {
        this.interestRate = interestRate;
    }
}

CreditAccount.java

The primary key of this table is also a foreign key to the superclass table and is 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.
Let's join a table with @PrimaryKeyJoinColumn annotation:
package net.javaguides.hibernate.entity;

import jakarta.persistence.Entity;

@Entity(name = "CreditAccount")
@PrimaryKeyJoinColumn(name = "account_id")
public class CreditAccount extends Account {

    private double creditLimit;

    public double getCreditLimit() {
        return creditLimit;
    }

    public void setCreditLimit(double creditLimit) {
        this.creditLimit = creditLimit;
    }
}

DebitAccount.java

Let's use the @PrimaryKeyJoinColumn annotation to join the table.
package net.javaguides.hibernate.entity;

import jakarta.persistence.Entity;

@Entity(name = "DebitAccount")
@PrimaryKeyJoinColumn(name = "account_id")
public class DebitAccount extends Account {

    private double overdraftFee;

    public double getOverdraftFee() {
        return overdraftFee;
    }

    public void setOverdraftFee(double overdraftFee) {
        this.overdraftFee = overdraftFee;
    }
}
Complete example at Hibernate JPA Joined Table Inheritance Example

4. Hibernate/JPA Table Per Class Inheritance

In a Table per class inheritance strategy, each concrete subclass has its own table containing both the subclass and the base class properties.
For example:

Account.java

package net.javaguides.hibernate.entity;

import jakarta.persistence.*;

@Entity(name = "Account")
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Account {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String owner;

    private double balance;

    private double interestRate;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getOwner() {
        return owner;
    }

    public void setOwner(String owner) {
        this.owner = owner;
    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    public double getInterestRate() {
        return interestRate;
    }

    public void setInterestRate(double interestRate) {
        this.interestRate = interestRate;
    }
}

CreditAccount.java

package net.javaguides.hibernate.entity;

import jakarta.persistence.Entity;

@Entity(name = "CreditAccount")
public class CreditAccount extends Account {

    private double creditLimit;

    public double getCreditLimit() {
        return creditLimit;
    }

    public void setCreditLimit(double creditLimit) {
        this.creditLimit = creditLimit;
    }
}

DebitAccount.java

package net.javaguides.hibernate.entity;

import jakarta.persistence.Entity;

@Entity(name = "DebitAccount")
public class DebitAccount extends Account {

    private double overdraftFee;

    public double getOverdraftFee() {
        return overdraftFee;
    }

    public void setOverdraftFee(double overdraftFee) {
        this.overdraftFee = overdraftFee;
    }
}
Complete example at Hibernate/JPA Table Per Class Inheritance Example

GitHub Repository

The complete source code of this article is available on my GitHub Repository - https://github.com/RameshMF/Hibernate-ORM-Tutorials

Conclusion

In this article,  we have discussed how JPA/Hibernate supports Inheritance mapping. We also looked into several inheritance strategies that the JPA specification provides.
You can learn more about Hibernate ORM Framework at Hibernate Tutorial

Comments