|
2 | 2 | title: "Table Inheritance Pattern in Java: Modeling Hierarchical Data in Relational Databases"
|
3 | 3 | shortTitle: Table Inheritance
|
4 | 4 | description: "Explore the Table Inheritance pattern in Java with real-world examples, database schema, and tutorials. Learn how to model class hierarchies elegantly in relational databases."
|
5 |
| -category: Structural |
| 5 | +category: Data access |
6 | 6 | language: en
|
7 | 7 | tag:
|
8 |
| - - Decoupling |
| 8 | + - Data access |
| 9 | + - Database |
| 10 | + - Inheritance |
| 11 | + - Persistence |
| 12 | + - Polymorphism |
9 | 13 | ---
|
10 | 14 |
|
| 15 | +## Also known as |
| 16 | + |
| 17 | +* Class Table Inheritance |
| 18 | +* Joined Table Inheritance |
| 19 | + |
11 | 20 | ## Intent of Table Inheritance Pattern
|
12 |
| -The Table Inheritance pattern models a class hierarchy in a relational database by creating |
13 |
| -separate tables for each class in the hierarchy. These tables share a common primary key, which in |
14 |
| -subclass tables also serves as a foreign key referencing the primary key of the base class table. |
15 |
| -This linkage maintains relationships and effectively represents the inheritance structure. This pattern |
16 |
| -enables the organization of complex data models, particularly when subclasses have unique properties |
17 |
| -that must be stored in distinct tables. |
18 | 21 |
|
19 |
| ---- |
| 22 | +Represent inheritance hierarchies in relational databases by mapping each class in a hierarchy to a database table. |
20 | 23 |
|
21 | 24 | ## Detailed Explanation of Table Inheritance Pattern with Real-World Examples
|
22 | 25 |
|
23 |
| -### Real-World Example |
24 |
| -Consider a **Vehicle Management System** with a `Vehicle` superclass and subclasses like `Car` and `Truck`. |
| 26 | +Real-world example |
25 | 27 |
|
26 |
| -- The **Vehicle Table** stores attributes common to all vehicles, such as `make`, `model`, and `year`. Its primary key (`id`) uniquely identifies each vehicle. |
27 |
| -- The **Car Table** and **Truck Table** store attributes specific to their respective types, such as `numberOfDoors` for cars and `payloadCapacity` for trucks. |
28 |
| -- The `id` column in the **Car Table** and **Truck Table** serves as both the primary key for those tables and a foreign key referencing the `id` in the **Vehicle Table**. |
| 28 | +> A classic real-world analogy for the Table Inheritance (Joined Table) pattern is managing employee records in an organization: |
| 29 | +> 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. |
| 30 | +> This structure mirrors the Table Inheritance pattern—shared fields in a common table and unique fields split into subclass-specific tables. |
29 | 31 |
|
30 |
| -This setup ensures each subclass entry corresponds to a base class entry, maintaining the inheritance relationship while keeping subclass-specific data in their own tables. |
| 32 | +In plain words |
31 | 33 |
|
32 |
| -### In Plain Words |
33 |
| -In table inheritance, each class in the hierarchy is represented by a separate table, which |
34 |
| -allows for a clear distinction between shared attributes (stored in the base class table) and |
35 |
| -specific attributes (stored in subclass tables). |
| 34 | +> 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. |
36 | 35 |
|
37 |
| -### Martin Fowler Says |
| 36 | +Martin Fowler says |
38 | 37 |
|
39 |
| -Relational databases don't support inheritance, which creates a mismatch when mapping objects. |
40 |
| -To fix this, Table Inheritance uses a separate table for each class in the hierarchy while maintaining |
41 |
| -relationships through foreign keys, making it easier to link the classes together in the database. |
| 38 | +> 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. |
42 | 39 |
|
43 |
| -For more detailed information, refer to Martin Fowler's article on [Class Table Inheritance](https://martinfowler.com/eaaCatalog/classTableInheritance.html). |
| 40 | +Mind map |
44 | 41 |
|
| 42 | + |
45 | 43 |
|
46 | 44 | ## Programmatic Example of Table Inheritance Pattern in Java
|
47 | 45 |
|
48 |
| - |
49 |
| -The `Vehicle` class will be the superclass, and we will have `Car` and `Truck` as subclasses that extend |
50 |
| -`Vehicle`. The `Vehicle` class will store common attributes, while `Car` and `Truck` will store |
51 |
| -attributes specific to those subclasses. |
| 46 | +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. |
52 | 47 |
|
53 | 48 | ### Key Aspects of the Pattern:
|
54 | 49 |
|
55 |
| -1. **Superclass (`Vehicle`)**: |
56 |
| - The `Vehicle` class stores attributes shared by all vehicle types, such as: |
57 |
| - - `make`: The manufacturer of the vehicle. |
58 |
| - - `model`: The model of the vehicle. |
59 |
| - - `year`: The year the vehicle was manufactured. |
60 |
| - - `id`: A unique identifier for the vehicle. |
| 50 | +**Superclass (`Vehicle`):** |
| 51 | + |
| 52 | +The superclass stores shared attributes: |
| 53 | + |
| 54 | +* `make`: Manufacturer of the vehicle. |
| 55 | +* `model`: Model of the vehicle. |
| 56 | +* `year`: Year of manufacture. |
| 57 | +* `id`: Unique identifier for the vehicle. |
61 | 58 |
|
62 |
| - These attributes are stored in the **`Vehicle` table** in the database. |
| 59 | +These common attributes will reside in a dedicated database table (`Vehicle` table). |
63 | 60 |
|
64 |
| -2. **Subclass (`Car` and `Truck`)**: |
65 |
| - Each subclass (`Car` and `Truck`) stores attributes specific to that vehicle type: |
66 |
| - - `Car`: Has an additional attribute `numberOfDoors` representing the number of doors the car has. |
67 |
| - - `Truck`: Has an additional attribute `payloadCapacity` representing the payload capacity of the truck. |
| 61 | +**Subclasses (`Car` and `Truck`):** |
68 | 62 |
|
69 |
| - These subclass-specific attributes are stored in the **`Car` and `Truck` tables**. |
| 63 | +Each subclass adds attributes specific to its type: |
70 | 64 |
|
71 |
| -3. **Foreign Key Relationship**: |
72 |
| - Each subclass (`Car` and `Truck`) contains the `id` field which acts as a **foreign key** that |
73 |
| -references the primary key (`id`) of the superclass (`Vehicle`). This foreign key ensures the |
74 |
| -relationship between the common attributes in the `Vehicle` table and the specific attributes in the |
75 |
| -subclass tables (`Car` and `Truck`). |
| 65 | +* `Car`: `numberOfDoors`, indicating how many doors the car has. |
| 66 | +* `Truck`: `payloadCapacity`, representing how much payload the truck can carry. |
76 | 67 |
|
| 68 | +Each subclass stores these specific attributes in their respective tables (`Car` and `Truck` tables). |
| 69 | + |
| 70 | +**Foreign Key Relationship:** |
| 71 | + |
| 72 | +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. |
| 73 | + |
| 74 | +### Java Implementation Using JPA Annotations: |
77 | 75 |
|
78 | 76 | ```java
|
79 |
| -/** |
80 |
| - * Superclass |
81 |
| - * Represents a generic vehicle with basic attributes like make, model, year, and ID. |
82 |
| - */ |
| 77 | +@Setter |
| 78 | +@Getter |
83 | 79 | public class Vehicle {
|
84 |
| - private String make; |
85 |
| - private String model; |
86 |
| - private int year; |
87 |
| - private int id; |
88 | 80 |
|
89 |
| - // Constructor, getters, and setters... |
| 81 | + private String make; |
| 82 | + private String model; |
| 83 | + private int year; |
| 84 | + private int id; |
| 85 | + |
| 86 | + public Vehicle(int year, String make, String model, int id) { |
| 87 | + this.make = make; |
| 88 | + this.model = model; |
| 89 | + this.year = year; |
| 90 | + this.id = id; |
| 91 | + } |
| 92 | + |
| 93 | + @Override |
| 94 | + public String toString() { |
| 95 | + return "Vehicle{" |
| 96 | + + "id=" |
| 97 | + + id |
| 98 | + + ", make='" |
| 99 | + + make |
| 100 | + + '\'' |
| 101 | + + ", model='" |
| 102 | + + model |
| 103 | + + '\'' |
| 104 | + + ", year=" |
| 105 | + + year |
| 106 | + + '}'; |
| 107 | + } |
90 | 108 | }
|
91 | 109 |
|
92 |
| -/** |
93 |
| - * Represents a car, which is a subclass of Vehicle. |
94 |
| - */ |
| 110 | +@Getter |
95 | 111 | public class Car extends Vehicle {
|
96 |
| - private int numberOfDoors; |
97 |
| - |
98 |
| - // Constructor, getters, and setters... |
| 112 | + private int numDoors; |
| 113 | + |
| 114 | + public Car(int year, String make, String model, int numDoors, int id) { |
| 115 | + super(year, make, model, id); |
| 116 | + if (numDoors <= 0) { |
| 117 | + throw new IllegalArgumentException("Number of doors must be positive."); |
| 118 | + } |
| 119 | + this.numDoors = numDoors; |
| 120 | + } |
| 121 | + |
| 122 | + public void setNumDoors(int doors) { |
| 123 | + if (doors <= 0) { |
| 124 | + throw new IllegalArgumentException("Number of doors must be positive."); |
| 125 | + } |
| 126 | + this.numDoors = doors; |
| 127 | + } |
| 128 | + |
| 129 | + @Override |
| 130 | + public String toString() { |
| 131 | + return "Car{" |
| 132 | + + "id=" |
| 133 | + + getId() |
| 134 | + + ", make='" |
| 135 | + + getMake() |
| 136 | + + '\'' |
| 137 | + + ", model='" |
| 138 | + + getModel() |
| 139 | + + '\'' |
| 140 | + + ", year=" |
| 141 | + + getYear() |
| 142 | + + ", numberOfDoors=" |
| 143 | + + getNumDoors() |
| 144 | + + '}'; |
| 145 | + } |
99 | 146 | }
|
100 | 147 |
|
101 |
| -/** |
102 |
| - * Represents a truck, which is a subclass of Vehicle. |
103 |
| - */ |
| 148 | +@Getter |
104 | 149 | public class Truck extends Vehicle {
|
105 |
| - private int payloadCapacity; |
106 |
| - |
107 |
| - // Constructor, getters, and setters... |
| 150 | + private double loadCapacity; |
| 151 | + |
| 152 | + public Truck(int year, String make, String model, double loadCapacity, int id) { |
| 153 | + super(year, make, model, id); |
| 154 | + if (loadCapacity <= 0) { |
| 155 | + throw new IllegalArgumentException("Load capacity must be positive."); |
| 156 | + } |
| 157 | + this.loadCapacity = loadCapacity; |
| 158 | + } |
| 159 | + |
| 160 | + public void setLoadCapacity(double capacity) { |
| 161 | + if (capacity <= 0) { |
| 162 | + throw new IllegalArgumentException("Load capacity must be positive."); |
| 163 | + } |
| 164 | + this.loadCapacity = capacity; |
| 165 | + } |
| 166 | + |
| 167 | + @Override |
| 168 | + public String toString() { |
| 169 | + return "Truck{" |
| 170 | + + "id=" |
| 171 | + + getId() |
| 172 | + + ", make='" |
| 173 | + + getMake() |
| 174 | + + '\'' |
| 175 | + + ", model='" |
| 176 | + + getModel() |
| 177 | + + '\'' |
| 178 | + + ", year=" |
| 179 | + + getYear() |
| 180 | + + ", payloadCapacity=" |
| 181 | + + getLoadCapacity() |
| 182 | + + '}'; |
| 183 | + } |
108 | 184 | }
|
109 | 185 | ```
|
110 | 186 |
|
| 187 | +### Explanation of the JPA annotations used above: |
111 | 188 |
|
| 189 | +* `@Entity`: Indicates that the class is a JPA entity mapped to a database table. |
| 190 | +* `@Inheritance(strategy = InheritanceType.JOINED)`: Configures joined table inheritance, meaning each class (superclass and subclasses) maps to its own table. |
| 191 | +* `@Table(name = "XYZ")`: Explicitly specifies the database table name for clarity. |
| 192 | +* `@Id`: Marks the primary key of the entity. |
| 193 | +* `@GeneratedValue(strategy = GenerationType.IDENTITY)`: Specifies auto-generation of primary key values by the database. |
112 | 194 |
|
113 |
| -## Table Inheritance Pattern Class Diagram |
| 195 | +### Database Structure Result: |
114 | 196 |
|
| 197 | +Applying this code will result in three database tables structured as follows: |
115 | 198 |
|
116 |
| -<img src="etc/class-diagram.png" width="400" height="500" /> |
| 199 | +**Vehicle table** |
| 200 | +* id |
| 201 | +* make |
| 202 | +* model |
| 203 | +* year |
117 | 204 |
|
| 205 | +**Car table** |
| 206 | +* id (FK to Vehicle) |
| 207 | +* numberOfDoors |
118 | 208 |
|
| 209 | +**Truck table** |
| 210 | +* id (FK to Vehicle) |
| 211 | +* payloadCapacity |
119 | 212 |
|
120 |
| - |
121 |
| - |
122 |
| - |
123 |
| -## Table Inheritance Pattern Database Schema |
124 |
| - |
125 |
| -### Vehicle Table |
126 |
| -| Column | Description | |
127 |
| -|--------|-------------------------------------| |
128 |
| -| id | Primary key | |
129 |
| -| make | The make of the vehicle | |
130 |
| -| model | The model of the vehicle | |
131 |
| -| year | The manufacturing year of the vehicle | |
132 |
| - |
133 |
| -### Car Table |
134 |
| -| Column | Description | |
135 |
| -|------------------|-------------------------------------| |
136 |
| -| id | Foreign key referencing `Vehicle(id)` | |
137 |
| -| numberOfDoors | Number of doors in the car | |
138 |
| - |
139 |
| -### Truck Table |
140 |
| -| Column | Description | |
141 |
| -|-------------------|-------------------------------------| |
142 |
| -| id | Foreign key referencing `Vehicle(id)` | |
143 |
| -| payloadCapacity | Payload capacity of the truck | |
144 |
| - |
145 |
| ---- |
| 213 | +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. |
146 | 214 |
|
147 | 215 | ## When to Use the Table Inheritance Pattern in Java
|
148 | 216 |
|
149 |
| -- When your application requires a clear mapping of an object-oriented class hierarchy to relational tables. |
150 |
| -- When subclasses have unique attributes that do not fit into a single base table. |
151 |
| -- When scalability and normalization of data are important considerations. |
152 |
| -- When you need to separate concerns and organize data in a way that each subclass has its own |
153 |
| -table but maintains relationships with the superclass. |
| 217 | +* When persisting an inheritance hierarchy of Java classes in a relational database. |
| 218 | +* Suitable when classes share common attributes but also have distinct fields. |
| 219 | +* Beneficial when polymorphic queries across subclasses are frequent. |
154 | 220 |
|
155 | 221 | ## Table Inheritance Pattern Java Tutorials
|
156 | 222 |
|
157 | 223 | - [Software Patterns Lexicon: Class Table Inheritance](https://softwarepatternslexicon.com/patterns-sql/4/4/2/)
|
158 | 224 | - [Martin Fowler: Class Table Inheritance](http://thierryroussel.free.fr/java/books/martinfowler/www.martinfowler.com/isa/classTableInheritance.html)
|
159 | 225 |
|
160 |
| ---- |
161 |
| - |
162 | 226 | ## Real-World Applications of Table Inheritance Pattern in Java
|
163 | 227 |
|
164 |
| -- **Vehicle Management System**: Used to store different types of vehicles like Car and Truck in separate tables but maintain a relationship through a common superclass `Vehicle`. |
165 |
| -- **E-Commerce Platforms**: Where different product types, such as Clothing, Electronics, and Furniture, are stored in separate tables with shared attributes in a superclass `Product`. |
| 228 | +* Hibernate ORM (`@Inheritance(strategy = InheritanceType.JOINED)` in Java) |
| 229 | +* EclipseLink (Joined Inheritance strategy in JPA) |
| 230 | +* Spring Data JPA applications modeling complex domain hierarchies. |
166 | 231 |
|
167 | 232 | ## Benefits and Trade-offs of Table Inheritance Pattern
|
168 | 233 |
|
169 |
| -### Benefits |
| 234 | +Benefits: |
170 | 235 |
|
171 |
| -- **Clear Structure**: Each class has its own table, making the data model easier to maintain and understand. |
172 |
| -- **Scalability**: Each subclass can be extended independently without affecting the other tables, making the system more scalable. |
173 |
| -- **Data Normalization**: Helps avoid data redundancy and keeps the schema normalized. |
| 236 | + * Normalized database schema reduces redundancy. |
| 237 | + * Clearly models class hierarchies at the database level. |
| 238 | + * Easier to implement polymorphic queries due to clear class distinctions. |
174 | 239 |
|
175 |
| -### Trade-offs |
| 240 | +Trade-offs: |
176 | 241 |
|
177 |
| -- **Multiple Joins**: Retrieving data that spans multiple subclasses may require joining multiple tables, which could lead to performance issues. |
178 |
| -- **Increased Complexity**: Managing relationships between tables and maintaining integrity can become more complex. |
179 |
| -- **Potential for Sparse Tables**: Subclasses with fewer attributes may end up with tables that have many null fields. |
| 242 | + * Increased complexity in database queries involving multiple joins. |
| 243 | + * Reduced performance for deep inheritance hierarchies due to costly joins. |
| 244 | + * Maintenance overhead increases with the complexity of inheritance structures. |
180 | 245 |
|
181 | 246 | ## Related Java Design Patterns
|
182 | 247 |
|
183 |
| -- **Single Table Inheritance** – A strategy where a single table is used to store all classes in an |
184 |
| -inheritance hierarchy. It stores all attributes of the class and its subclasses in one table. |
185 |
| -- **Singleton Pattern** – Used when a class needs to have only one instance. |
186 |
| - |
| 248 | +* [Single Table Inheritance](https://java-design-patterns.com/patterns/single-table-inheritance/): Alternative strategy mapping an entire class hierarchy into a single database table, useful when fewer joins are preferred at the cost of nullable columns. |
| 249 | +* Concrete Table Inheritance – Each subclass has its own standalone table; related in providing an alternate approach to storing inheritance hierarchies. |
187 | 250 |
|
188 | 251 | ## References and Credits
|
189 | 252 |
|
190 |
| -- **Martin Fowler** - [*Patterns of Enterprise Application Architecture*](https://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420) |
191 |
| -- **Java Persistence with Hibernate** - [Link to book](https://www.amazon.com/Java-Persistence-Hibernate-Christian-Bauer/dp/193239469X) |
192 |
| -- **Object-Relational Mapping on Wikipedia** - [Link to article](https://en.wikipedia.org/wiki/Object-relational_mapping) |
| 253 | +* [Java Persistence with Hibernate](https://amzn.to/44tP1ox) |
| 254 | +* [Object-Relational Mapping (Wikipedia)](https://en.wikipedia.org/wiki/Object-relational_mapping) |
| 255 | +* [Patterns of Enterprise Application Architecture](https://amzn.to/3WfKBPR) |
| 256 | +* [Pro JPA 2: Mastering the Java Persistence API](https://amzn.to/4b7UoMC) |
0 commit comments