Tutorials Logic, IN +91 8092939553 info@tutorialslogic.com
FAQs Support
Navigation
Home About Us Contact Us Blogs FAQs
Tutorials
All Tutorials
Services
Academic Projects Resume Writing Interview Questions Website Development
Compiler Tutorials

Hibernate Inheritance Mapping

Inheritance Strategies Overview

Hibernate supports mapping Java class hierarchies to relational database tables. There are four strategies:

StrategyTable StructureProsCons
SINGLE_TABLEOne table for all classesBest performance, simple queriesMany nullable columns, no NOT NULL constraints on subclass fields
TABLE_PER_CLASSOne table per concrete classNo joins needed for single typePolymorphic queries use UNION (slow), duplicate columns
JOINEDOne table per class (parent + subclass tables)Normalized, no nullsJoins required for every query
@MappedSuperclassNo table for superclassShare fields without polymorphismCannot query the superclass type
SINGLE_TABLE and TABLE_PER_CLASS Strategies
import jakarta.persistence.*;

// SINGLE_TABLE: all subclasses stored in one table
// A discriminator column identifies the type
@Entity
@Table(name = "payments")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "payment_type",
                     discriminatorType = DiscriminatorType.STRING)
public abstract class Payment {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private double amount;
    private java.time.LocalDate paymentDate;
    // getters/setters...
}

@Entity
@DiscriminatorValue("CREDIT_CARD")
public class CreditCardPayment extends Payment {
    private String cardNumber;
    private String cardHolder;
    // Stored in 'payments' table with payment_type = 'CREDIT_CARD'
    // cardNumber and cardHolder are nullable for other types
}

@Entity
@DiscriminatorValue("BANK_TRANSFER")
public class BankTransferPayment extends Payment {
    private String bankAccount;
    private String bankCode;
    // Stored in 'payments' table with payment_type = 'BANK_TRANSFER'
}

// Polymorphic query — fetches all payment types
// SELECT * FROM payments  (single table, no joins)
// List<Payment> all = em.createQuery("FROM Payment", Payment.class).getResultList();
// TABLE_PER_CLASS: each concrete class has its own complete table
// No discriminator column needed

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Vehicle {
    @Id @GeneratedValue(strategy = GenerationType.AUTO) // TABLE_PER_CLASS requires AUTO
    private Long id;
    private String manufacturer;
    private int year;
}

@Entity
@Table(name = "cars")
public class Car extends Vehicle {
    private int numDoors;
    private String fuelType;
    // Table 'cars' has: id, manufacturer, year, num_doors, fuel_type
}

@Entity
@Table(name = "motorcycles")
public class Motorcycle extends Vehicle {
    private String type; // sport, cruiser, touring
    // Table 'motorcycles' has: id, manufacturer, year, type
}

// Polymorphic query uses UNION ALL (can be slow):
// SELECT id, manufacturer, year, num_doors, null AS type FROM cars
// UNION ALL
// SELECT id, manufacturer, year, null AS num_doors, type FROM motorcycles

JOINED Strategy and @MappedSuperclass

JOINED Strategy and @MappedSuperclass
// JOINED: normalized — parent table + subclass tables joined by FK
// Best for data integrity; requires JOIN for every query

@Entity
@Table(name = "employees")
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class Employee {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String email;
    private double baseSalary;
}

@Entity
@Table(name = "full_time_employees")
@PrimaryKeyJoinColumn(name = "employee_id") // FK to employees.id
public class FullTimeEmployee extends Employee {
    private double bonus;
    private int vacationDays;
    // Query: SELECT e.*, f.bonus, f.vacation_days
    //        FROM employees e JOIN full_time_employees f ON e.id = f.employee_id
}

@Entity
@Table(name = "part_time_employees")
@PrimaryKeyJoinColumn(name = "employee_id")
public class PartTimeEmployee extends Employee {
    private double hourlyRate;
    private int hoursPerWeek;
}
// @MappedSuperclass: NOT an entity — no table created for it
// Subclasses inherit fields but cannot be queried polymorphically

import jakarta.persistence.*;
import java.time.LocalDateTime;

@MappedSuperclass
public abstract class BaseEntity {
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "created_at", updatable = false)
    private LocalDateTime createdAt;

    @Column(name = "updated_at")
    private LocalDateTime updatedAt;

    @PrePersist
    protected void onCreate() {
        createdAt = updatedAt = LocalDateTime.now();
    }

    @PreUpdate
    protected void onUpdate() {
        updatedAt = LocalDateTime.now();
    }

    // getters/setters...
}

// Each subclass gets its own table with the inherited fields
@Entity
@Table(name = "products")
public class Product extends BaseEntity {
    private String name;
    private double price;
    // Table 'products': id, created_at, updated_at, name, price
}

@Entity
@Table(name = "categories")
public class Category extends BaseEntity {
    private String title;
    // Table 'categories': id, created_at, updated_at, title
}

// NOTE: Cannot query BaseEntity directly:
// em.createQuery("FROM BaseEntity") // ERROR — not an entity!
Polymorphic Queries
@Repository
@Transactional(readOnly = true)
public class PaymentRepository {

    @PersistenceContext
    private EntityManager em;

    // Polymorphic query — returns all Payment subtypes
    public List<Payment> getAllPayments() {
        return em.createQuery("FROM Payment p ORDER BY p.paymentDate DESC",
                              Payment.class)
                 .getResultList();
    }

    // Query only a specific subtype
    public List<CreditCardPayment> getCreditCardPayments() {
        return em.createQuery(
                "FROM CreditCardPayment p WHERE p.amount > :min",
                CreditCardPayment.class)
                .setParameter("min", 100.0)
                .getResultList();
    }

    // Use TYPE() function to filter by subtype in polymorphic query
    public List<Payment> getCreditCardAndBankPayments() {
        return em.createQuery(
                "FROM Payment p WHERE TYPE(p) IN (CreditCardPayment, BankTransferPayment)",
                Payment.class)
                .getResultList();
    }

    // instanceof check in Java after fetching
    public void processPayments() {
        List<Payment> payments = getAllPayments();
        for (Payment p : payments) {
            if (p instanceof CreditCardPayment cc) {
                System.out.println("Card: " + cc.getCardNumber());
            } else if (p instanceof BankTransferPayment bt) {
                System.out.println("Bank: " + bt.getBankAccount());
            }
        }
    }
}

Ready to Level Up Your Skills?

Explore 500+ free tutorials across 20+ languages and frameworks.