diff --git a/table-inheritance/README.md b/table-inheritance/README.md index 53e9b2ce0..fc9f75002 100644 --- a/table-inheritance/README.md +++ b/table-inheritance/README.md @@ -2,191 +2,255 @@ title: "Table Inheritance Pattern in Java: Modeling Hierarchical Data in Relational Databases" shortTitle: Table Inheritance 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." -category: Structural +category: Data access language: en tag: - - Decoupling + - Data access + - Database + - Inheritance + - Persistence + - Polymorphism --- +## Also known as + +* Class Table Inheritance +* Joined Table Inheritance + ## Intent of Table Inheritance Pattern -The Table Inheritance pattern models a class hierarchy in a relational database by creating -separate tables for each class in the hierarchy. These tables share a common primary key, which in -subclass tables also serves as a foreign key referencing the primary key of the base class table. -This linkage maintains relationships and effectively represents the inheritance structure. This pattern -enables the organization of complex data models, particularly when subclasses have unique properties -that must be stored in distinct tables. ---- +Represent inheritance hierarchies in relational databases by mapping each class in a hierarchy to a database table. ## Detailed Explanation of Table Inheritance Pattern with Real-World Examples -### Real-World Example -Consider a **Vehicle Management System** with a `Vehicle` superclass and subclasses like `Car` and `Truck`. +Real-world example -- The **Vehicle Table** stores attributes common to all vehicles, such as `make`, `model`, and `year`. Its primary key (`id`) uniquely identifies each vehicle. -- The **Car Table** and **Truck Table** store attributes specific to their respective types, such as `numberOfDoors` for cars and `payloadCapacity` for trucks. -- 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**. +> 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. -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. +In plain words -### In Plain Words -In table inheritance, each class in the hierarchy is represented by a separate table, which -allows for a clear distinction between shared attributes (stored in the base class table) and -specific attributes (stored in subclass tables). +> 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 +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. +> 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. -For more detailed information, refer to Martin Fowler's article on [Class Table Inheritance](https://martinfowler.com/eaaCatalog/classTableInheritance.html). +Mind map +![Table Inheritance Pattern Mind Map](./etc/table-inheritance-mind-map.png) ## Programmatic Example of Table Inheritance Pattern in Java - -The `Vehicle` class will be the superclass, and we will have `Car` and `Truck` as subclasses that extend -`Vehicle`. The `Vehicle` class will store common attributes, while `Car` and `Truck` will store -attributes specific to those subclasses. +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. ### Key Aspects of the Pattern: -1. **Superclass (`Vehicle`)**: - The `Vehicle` class stores attributes shared by all vehicle types, such as: - - `make`: The manufacturer of the vehicle. - - `model`: The model of the vehicle. - - `year`: The year the vehicle was manufactured. - - `id`: A unique identifier for the vehicle. +**Superclass (`Vehicle`):** - These attributes are stored in the **`Vehicle` table** in the database. +The superclass stores shared attributes: -2. **Subclass (`Car` and `Truck`)**: - Each subclass (`Car` and `Truck`) stores attributes specific to that vehicle type: - - `Car`: Has an additional attribute `numberOfDoors` representing the number of doors the car has. - - `Truck`: Has an additional attribute `payloadCapacity` representing the payload capacity of the truck. +* `make`: Manufacturer of the vehicle. +* `model`: Model of the vehicle. +* `year`: Year of manufacture. +* `id`: Unique identifier for the vehicle. - These subclass-specific attributes are stored in the **`Car` and `Truck` tables**. +These common attributes will reside in a dedicated database table (`Vehicle` table). -3. **Foreign Key Relationship**: - Each subclass (`Car` and `Truck`) contains the `id` field which acts as a **foreign key** that -references the primary key (`id`) of the superclass (`Vehicle`). This foreign key ensures the -relationship between the common attributes in the `Vehicle` table and the specific attributes in the -subclass tables (`Car` and `Truck`). +**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. + +### Java Implementation Using JPA Annotations: ```java -/** - * Superclass - * Represents a generic vehicle with basic attributes like make, model, year, and ID. - */ +@Setter +@Getter public class Vehicle { - private String make; - private String model; - private int year; - private int id; - // Constructor, getters, and setters... + 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 + + '}'; + } } -/** - * Represents a car, which is a subclass of Vehicle. - */ +@Getter public class Car extends Vehicle { - private int numberOfDoors; + private int numDoors; - // Constructor, getters, and setters... + 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() + + '}'; + } } -/** - * Represents a truck, which is a subclass of Vehicle. - */ +@Getter public class Truck extends Vehicle { - private int payloadCapacity; + private double loadCapacity; - // Constructor, getters, and setters... + 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() + + '}'; + } } ``` +### Explanation of the JPA annotations used above: +* `@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. -## Table Inheritance Pattern Class Diagram +### Database Structure Result: +Applying this code will result in three database tables structured as follows: - +**Vehicle table** +* id +* make +* model +* year +**Car table** +* id (FK to Vehicle) +* numberOfDoors +**Truck table** +* id (FK to Vehicle) +* payloadCapacity - - - -## Table Inheritance Pattern Database Schema - -### Vehicle Table -| Column | Description | -|--------|-------------------------------------| -| id | Primary key | -| make | The make of the vehicle | -| model | The model of the vehicle | -| year | The manufacturing year of the vehicle | - -### Car Table -| Column | Description | -|------------------|-------------------------------------| -| id | Foreign key referencing `Vehicle(id)` | -| numberOfDoors | Number of doors in the car | - -### Truck Table -| Column | Description | -|-------------------|-------------------------------------| -| id | Foreign key referencing `Vehicle(id)` | -| payloadCapacity | Payload capacity of the truck | - ---- +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. ## When to Use the Table Inheritance Pattern in Java -- When your application requires a clear mapping of an object-oriented class hierarchy to relational tables. -- When subclasses have unique attributes that do not fit into a single base table. -- When scalability and normalization of data are important considerations. -- When you need to separate concerns and organize data in a way that each subclass has its own -table but maintains relationships with the superclass. +* When persisting an inheritance hierarchy of Java classes in a relational database. +* Suitable when classes share common attributes but also have distinct fields. +* Beneficial when polymorphic queries across subclasses are frequent. ## Table Inheritance Pattern Java Tutorials - [Software Patterns Lexicon: Class Table Inheritance](https://softwarepatternslexicon.com/patterns-sql/4/4/2/) - [Martin Fowler: Class Table Inheritance](http://thierryroussel.free.fr/java/books/martinfowler/www.martinfowler.com/isa/classTableInheritance.html) ---- - ## Real-World Applications of Table Inheritance Pattern in Java -- **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`. -- **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`. +* Hibernate ORM (`@Inheritance(strategy = InheritanceType.JOINED)` in Java) +* EclipseLink (Joined Inheritance strategy in JPA) +* Spring Data JPA applications modeling complex domain hierarchies. ## Benefits and Trade-offs of Table Inheritance Pattern -### Benefits +Benefits: -- **Clear Structure**: Each class has its own table, making the data model easier to maintain and understand. -- **Scalability**: Each subclass can be extended independently without affecting the other tables, making the system more scalable. -- **Data Normalization**: Helps avoid data redundancy and keeps the schema normalized. + * Normalized database schema reduces redundancy. + * Clearly models class hierarchies at the database level. + * Easier to implement polymorphic queries due to clear class distinctions. -### Trade-offs +Trade-offs: -- **Multiple Joins**: Retrieving data that spans multiple subclasses may require joining multiple tables, which could lead to performance issues. -- **Increased Complexity**: Managing relationships between tables and maintaining integrity can become more complex. -- **Potential for Sparse Tables**: Subclasses with fewer attributes may end up with tables that have many null fields. + * Increased complexity in database queries involving multiple joins. + * Reduced performance for deep inheritance hierarchies due to costly joins. + * Maintenance overhead increases with the complexity of inheritance structures. ## Related Java Design Patterns -- **Single Table Inheritance** – A strategy where a single table is used to store all classes in an -inheritance hierarchy. It stores all attributes of the class and its subclasses in one table. -- **Singleton Pattern** – Used when a class needs to have only one instance. - +* [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. +* Concrete Table Inheritance – Each subclass has its own standalone table; related in providing an alternate approach to storing inheritance hierarchies. ## References and Credits -- **Martin Fowler** - [*Patterns of Enterprise Application Architecture*](https://www.amazon.com/Patterns-Enterprise-Application-Architecture-Martin/dp/0321127420) -- **Java Persistence with Hibernate** - [Link to book](https://www.amazon.com/Java-Persistence-Hibernate-Christian-Bauer/dp/193239469X) -- **Object-Relational Mapping on Wikipedia** - [Link to article](https://en.wikipedia.org/wiki/Object-relational_mapping) +* [Java Persistence with Hibernate](https://amzn.to/44tP1ox) +* [Object-Relational Mapping (Wikipedia)](https://en.wikipedia.org/wiki/Object-relational_mapping) +* [Patterns of Enterprise Application Architecture](https://amzn.to/3WfKBPR) +* [Pro JPA 2: Mastering the Java Persistence API](https://amzn.to/4b7UoMC) diff --git a/table-inheritance/etc/table-inheritance-mind-map.png b/table-inheritance/etc/table-inheritance-mind-map.png new file mode 100644 index 000000000..c403077b6 Binary files /dev/null and b/table-inheritance/etc/table-inheritance-mind-map.png differ diff --git a/table-inheritance/src/test/java/AppTest.java b/table-inheritance/src/test/java/com/iluwatar/table/inheritance/AppTest.java similarity index 53% rename from table-inheritance/src/test/java/AppTest.java rename to table-inheritance/src/test/java/com/iluwatar/table/inheritance/AppTest.java index 1d8e27142..8cbae5b2b 100644 --- a/table-inheritance/src/test/java/AppTest.java +++ b/table-inheritance/src/test/java/com/iluwatar/table/inheritance/AppTest.java @@ -22,9 +22,33 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +package com.iluwatar.table.inheritance; /* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + import static org.junit.jupiter.api.Assertions.assertTrue; -import com.iluwatar.table.inheritance.App; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.util.logging.ConsoleHandler; diff --git a/table-inheritance/src/test/java/VehicleDatabaseTest.java b/table-inheritance/src/test/java/com/iluwatar/table/inheritance/VehicleDatabaseTest.java similarity index 75% rename from table-inheritance/src/test/java/VehicleDatabaseTest.java rename to table-inheritance/src/test/java/com/iluwatar/table/inheritance/VehicleDatabaseTest.java index 71461f990..9c290fd5c 100644 --- a/table-inheritance/src/test/java/VehicleDatabaseTest.java +++ b/table-inheritance/src/test/java/com/iluwatar/table/inheritance/VehicleDatabaseTest.java @@ -22,14 +22,35 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +package com.iluwatar.table.inheritance; /* + * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt). + * + * The MIT License + * Copyright © 2014-2022 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertThrows; -import com.iluwatar.table.inheritance.Car; -import com.iluwatar.table.inheritance.Truck; -import com.iluwatar.table.inheritance.Vehicle; -import com.iluwatar.table.inheritance.VehicleDatabase; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -43,7 +64,7 @@ class VehicleDatabaseTest { /** Sets up a new instance of {@link VehicleDatabase} before each test. */ @BeforeEach - public void setUp() { + void setUp() { vehicleDatabase = new VehicleDatabase(); }