Hibernate Inheritance Mapping
Inheritance Strategies Overview
Hibernate supports mapping Java class hierarchies to relational database tables. There are four strategies:
| Strategy | Table Structure | Pros | Cons |
|---|---|---|---|
| SINGLE_TABLE | One table for all classes | Best performance, simple queries | Many nullable columns, no NOT NULL constraints on subclass fields |
| TABLE_PER_CLASS | One table per concrete class | No joins needed for single type | Polymorphic queries use UNION (slow), duplicate columns |
| JOINED | One table per class (parent + subclass tables) | Normalized, no nulls | Joins required for every query |
| @MappedSuperclass | No table for superclass | Share fields without polymorphism | Cannot query the superclass type |
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: 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!
@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.