table-inheritance/README.md
Represent inheritance hierarchies in relational databases by mapping each class in a hierarchy to a database table.
Real-world example
A classic real-world analogy for the Table Inheritance (Joined Table) pattern is managing employee records in an organization: Imagine a company's database storing information about employees. All employees have common attributes (name, employee ID, hire date), stored in a general "Employee" table. However, the company also has different types of employees: Full-time Employees (with a salary and benefits) and Contractors (hourly rate, contract duration). Each employee type has distinct data stored in separate specialized tables ("FullTimeEmployee" and "Contractor"), which reference the main "Employee" table. This structure mirrors the Table Inheritance pattern—shared fields in a common table and unique fields split into subclass-specific tables.
In plain words
The Table Inheritance pattern maps each class within an inheritance hierarchy to its own database table, storing common attributes in a base table and subclass-specific attributes in separate joined tables.
Martin Fowler says
Relational databases don't support inheritance, which creates a mismatch when mapping objects. To fix this, Table Inheritance uses a separate table for each class in the hierarchy while maintaining relationships through foreign keys, making it easier to link the classes together in the database.
Mind map
The Vehicle class will be the superclass, and we will have subclasses Car and Truck that extend Vehicle. The superclass Vehicle stores common attributes, while subclasses store their own specific attributes.
Superclass (Vehicle):
The superclass stores shared attributes:
make: Manufacturer of the vehicle.model: Model of the vehicle.year: Year of manufacture.id: Unique identifier for the vehicle.These common attributes will reside in a dedicated database table (Vehicle table).
Subclasses (Car and Truck):
Each subclass adds attributes specific to its type:
Car: numberOfDoors, indicating how many doors the car has.Truck: payloadCapacity, representing how much payload the truck can carry.Each subclass stores these specific attributes in their respective tables (Car and Truck tables).
Foreign Key Relationship:
Each subclass table references the superclass table via a foreign key. The subclass's id links to the primary key of the superclass, thus connecting common and subclass-specific data.
@Setter
@Getter
public class Vehicle {
private String make;
private String model;
private int year;
private int id;
public Vehicle(int year, String make, String model, int id) {
this.make = make;
this.model = model;
this.year = year;
this.id = id;
}
@Override
public String toString() {
return "Vehicle{"
+ "id="
+ id
+ ", make='"
+ make
+ '\''
+ ", model='"
+ model
+ '\''
+ ", year="
+ year
+ '}';
}
}
@Getter
public class Car extends Vehicle {
private int numDoors;
public Car(int year, String make, String model, int numDoors, int id) {
super(year, make, model, id);
if (numDoors <= 0) {
throw new IllegalArgumentException("Number of doors must be positive.");
}
this.numDoors = numDoors;
}
public void setNumDoors(int doors) {
if (doors <= 0) {
throw new IllegalArgumentException("Number of doors must be positive.");
}
this.numDoors = doors;
}
@Override
public String toString() {
return "Car{"
+ "id="
+ getId()
+ ", make='"
+ getMake()
+ '\''
+ ", model='"
+ getModel()
+ '\''
+ ", year="
+ getYear()
+ ", numberOfDoors="
+ getNumDoors()
+ '}';
}
}
@Getter
public class Truck extends Vehicle {
private double loadCapacity;
public Truck(int year, String make, String model, double loadCapacity, int id) {
super(year, make, model, id);
if (loadCapacity <= 0) {
throw new IllegalArgumentException("Load capacity must be positive.");
}
this.loadCapacity = loadCapacity;
}
public void setLoadCapacity(double capacity) {
if (capacity <= 0) {
throw new IllegalArgumentException("Load capacity must be positive.");
}
this.loadCapacity = capacity;
}
@Override
public String toString() {
return "Truck{"
+ "id="
+ getId()
+ ", make='"
+ getMake()
+ '\''
+ ", model='"
+ getModel()
+ '\''
+ ", year="
+ getYear()
+ ", payloadCapacity="
+ getLoadCapacity()
+ '}';
}
}
@Entity: Indicates that the class is a JPA entity mapped to a database table.@Inheritance(strategy = InheritanceType.JOINED): Configures joined table inheritance, meaning each class (superclass and subclasses) maps to its own table.@Table(name = "XYZ"): Explicitly specifies the database table name for clarity.@Id: Marks the primary key of the entity.@GeneratedValue(strategy = GenerationType.IDENTITY): Specifies auto-generation of primary key values by the database.Applying this code will result in three database tables structured as follows:
Vehicle table
Car table
Truck table
This approach clearly represents the Table Inheritance (Joined Table) pattern, with common attributes centrally managed in the superclass table and subclass-specific attributes cleanly separated in their own tables.
@Inheritance(strategy = InheritanceType.JOINED) in Java)Benefits:
Trade-offs: