Skip to content

Commit b2c7410

Browse files
authored
feat: #1316 Single Table Inheritance pattern implemented (#2632)
* implemented single table inheritance * added the Java documentation for the pattern * updated code files as per the standards * added the puml and png diagram for the file * added README.md * #1316 Single Table Inheritance pattern implemented * resolved the Checkstyle violations * removed the tests due to build failure * updated the code as per review * resolved Checkstyle violations
1 parent 79d41d9 commit b2c7410

File tree

16 files changed

+811
-0
lines changed

16 files changed

+811
-0
lines changed

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@
210210
<module>crtp</module>
211211
<module>log-aggregation</module>
212212
<module>health-check</module>
213+
<module>single-table-inheritance</module>
213214
</modules>
214215
<repositories>
215216
<repository>

single-table-inheritance/README.md

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
---
2+
title: Single Table Inheritance Pattern
3+
category: Structural
4+
language: en
5+
tag:
6+
- Data access
7+
---
8+
9+
## Single Table Inheritance(STI)
10+
11+
## Intent
12+
13+
Represents an inheritance hierarchy of classes as a single table that has columns for all the fields of the various classes.
14+
15+
## Explanation
16+
17+
Real-world example
18+
19+
> There can be many different types of vehicles in this world but all of them
20+
> come under the single umbrella of Vehicle
21+
22+
In plain words
23+
24+
> It maps each instance of class in an inheritance tree into a single table.
25+
26+
Wikipedia says
27+
28+
> Single table inheritance is a way to emulate object-oriented inheritance in a relational database.
29+
> When mapping from a database table to an object in an object-oriented language,
30+
> a field in the database identifies what class in the hierarchy the object belongs to.
31+
> All fields of all the classes are stored in the same table, hence the name "Single Table Inheritance".
32+
33+
**Programmatic Example**
34+
35+
Baeldung - Hibernate Inheritance
36+
37+
> We can define the strategy we want to use by adding the @Inheritance annotation to the superclass:
38+
39+
```java
40+
@Entity
41+
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
42+
public class MyProduct {
43+
@Id
44+
private long productId;
45+
private String name;
46+
47+
// constructor, getters, setters
48+
}
49+
```
50+
51+
The identifier of the entities is also defined in the superclass.
52+
53+
Then we can add the subclass entities:
54+
55+
```java
56+
@Entity
57+
public class Book extends MyProduct {
58+
private String author;
59+
}
60+
```
61+
62+
```java
63+
@Entity
64+
public class Pen extends MyProduct {
65+
private String color;
66+
}
67+
```
68+
Discriminator Values
69+
70+
- Since the records for all entities will be in the same table, Hibernate needs a way to differentiate between them.
71+
72+
- By default, this is done through a discriminator column called DTYPE that has the name of the entity as a value.
73+
74+
- To customize the discriminator column, we can use the @DiscriminatorColumn annotation:
75+
76+
```java
77+
@Entity(name="products")
78+
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
79+
@DiscriminatorColumn(name="product_type",
80+
discriminatorType = DiscriminatorType.INTEGER)
81+
public class MyProduct {
82+
// ...
83+
}
84+
```
85+
- Here we’ve chosen to differentiate MyProduct subclass entities by an integer column called product_type.
86+
87+
- Next, we need to tell Hibernate what value each subclass record will have for the product_type column:
88+
89+
```java
90+
@Entity
91+
@DiscriminatorValue("1")
92+
public class Book extends MyProduct {
93+
// ...
94+
}
95+
```
96+
```java
97+
@Entity
98+
@DiscriminatorValue("2")
99+
public class Pen extends MyProduct {
100+
// ...
101+
}
102+
```
103+
104+
- Hibernate adds two other predefined values that the annotation can take — null and not null:
105+
106+
- @DiscriminatorValue(“null”) means that any row without a discriminator value will be mapped to the entity class with this annotation; this can be applied to the root class of the hierarchy.
107+
- @DiscriminatorValue(“not null”) – Any row with a discriminator value not matching any of the ones associated with entity definitions will be mapped to the class with this annotation.
108+
109+
110+
## Class diagram
111+
112+
![alt text](./etc/single-table-inheritance.urm.png "Singleton pattern class diagram")
113+
114+
## Applicability
115+
116+
Use the Singleton pattern when
117+
118+
* Use STI When The Subclasses Have The Same Fields/Columns But Different Behavior
119+
- A good indication that STI is right is when the different subclasses have the same fields/columns but different methods. In the accounts example above, we expect all the columns in the database to be used by each subclass. Otherwise, there will be a lot of null columns in the database.
120+
<br><br>
121+
* Use STI When We Expect To Perform Queries Across All Subclasses
122+
- Another good indication STI is right is if we expect to perform queries across all classes. For example, if we want to find the top 10 accounts with the highest balances across all types, STI allows lets us use just one query, whereas MTI will require in memory manipulation.
123+
124+
125+
### Tutorials
126+
127+
- <a href ="https://www.youtube.com/watch?v=M5YrLtAHtOo" >Java Brains - Single Table Inheritance</a>
128+
129+
## Consequences
130+
131+
* Fields are sometimes relevant and sometimes not, which can be confusing
132+
to people using the tables directly.
133+
* Columns used only by some subclasses lead to wasted space in the database.
134+
How much this is actually a problem depends on the specific data
135+
characteristics and how well the database compresses empty columns.
136+
Oracle, for example, is very efficient in trimming wasted space, particularly
137+
if you keep your optional columns to the right side of the database
138+
table. Each database has its own tricks for this.
139+
* The single table may end up being too large, with many indexes and frequent
140+
locking, which may hurt performance. You can avoid this by having
141+
separate index tables that either list keys of rows that have a certain property
142+
or that copy a subset of fields relevant to an index.
143+
* You only have a single namespace for fields, so you have to be sure that
144+
you don’t use the same name for different fields. Compound names with
145+
the name of the class as a prefix or suffix help here.
146+
147+
## Related patterns
148+
149+
* MappedSuperclass
150+
* Single Table
151+
* Joined Table
152+
* Table per Class
153+
154+
## Credits
155+
156+
* [Single Table Inheritance - martinFowler.com](https://www.martinfowler.com/eaaCatalog/singleTableInheritance.html)
157+
* [Patterns of Enterprise Application Architecture](https://books.google.co.in/books?id=vqTfNFDzzdIC&pg=PA278&redir_esc=y#v=onepage&q&f=false)
138 KB
Loading
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
@startuml
2+
package com.iluwatar.repository {
3+
interface VehicleRepository {
4+
}
5+
}
6+
package com.iluwatar.service {
7+
class VehicleService {
8+
- vehicleRepository : VehicleRepository
9+
+ VehicleService(vehicleRepository : VehicleRepository)
10+
+ deleteVehicle(vehicle : Vehicle)
11+
+ getAllVehicles() : List<Vehicle>
12+
+ getVehicle(vehicleId : int) : Vehicle
13+
+ saveVehicle(vehicle : Vehicle) : Vehicle
14+
+ updateVehicle(vehicle : Vehicle) : Vehicle
15+
}
16+
}
17+
package com.iluwatar.entity {
18+
class Car {
19+
- engineCapacity : int
20+
+ Car()
21+
+ Car(manufacturer : String, model : String, noOfPassengers : int, engineCapacity : int)
22+
# canEqual(other : Object) : boolean
23+
+ equals(o : Object) : boolean
24+
+ getEngineCapacity() : int
25+
+ hashCode() : int
26+
+ setEngineCapacity(engineCapacity : int)
27+
+ toString() : String
28+
}
29+
class Freighter {
30+
- flightLength : double
31+
+ Freighter()
32+
+ Freighter(manufacturer : String, model : String, countOfSeats : int, loadCapacity : int, flightLength : double)
33+
# canEqual(other : Object) : boolean
34+
+ equals(o : Object) : boolean
35+
+ getFlightLength() : double
36+
+ hashCode() : int
37+
+ setFlightLength(flightLength : double)
38+
+ toString() : String
39+
}
40+
abstract class PassengerVehicle {
41+
- noOfPassengers : int
42+
+ PassengerVehicle()
43+
+ PassengerVehicle(manufacturer : String, model : String, noOfPassengers : int)
44+
# canEqual(other : Object) : boolean
45+
+ equals(o : Object) : boolean
46+
+ getNoOfPassengers() : int
47+
+ hashCode() : int
48+
+ setNoOfPassengers(noOfPassengers : int)
49+
+ toString() : String
50+
}
51+
class Train {
52+
- noOfCarriages : int
53+
+ Train()
54+
+ Train(manufacturer : String, model : String, noOfPassengers : int, noOfCarriages : int)
55+
# canEqual(other : Object) : boolean
56+
+ equals(o : Object) : boolean
57+
+ getNoOfCarriages() : int
58+
+ hashCode() : int
59+
+ setNoOfCarriages(noOfCarriages : int)
60+
+ toString() : String
61+
}
62+
abstract class TransportVehicle {
63+
- loadCapacity : int
64+
+ TransportVehicle()
65+
+ TransportVehicle(manufacturer : String, model : String, loadCapacity : int)
66+
# canEqual(other : Object) : boolean
67+
+ equals(o : Object) : boolean
68+
+ getLoadCapacity() : int
69+
+ hashCode() : int
70+
+ setLoadCapacity(loadCapacity : int)
71+
+ toString() : String
72+
}
73+
class Truck {
74+
+ towingCapacity : int
75+
+ Truck()
76+
+ Truck(manufacturer : String, model : String, loadCapacity : int, towingCapacity : int)
77+
# canEqual(other : Object) : boolean
78+
+ equals(o : Object) : boolean
79+
+ getTowingCapacity() : int
80+
+ hashCode() : int
81+
+ setTowingCapacity(towingCapacity : int)
82+
+ toString() : String
83+
}
84+
abstract class Vehicle {
85+
- manufacturer : String
86+
- model : String
87+
- vehicleId : int
88+
+ Vehicle()
89+
+ Vehicle(manufacturer : String, model : String)
90+
+ Vehicle(vehicleId : int, manufacturer : String, model : String)
91+
# canEqual(other : Object) : boolean
92+
+ equals(o : Object) : boolean
93+
+ getManufacturer() : String
94+
+ getModel() : String
95+
+ getVehicleId() : int
96+
+ hashCode() : int
97+
+ setManufacturer(manufacturer : String)
98+
+ setModel(model : String)
99+
+ setVehicleId(vehicleId : int)
100+
+ toString() : String
101+
}
102+
}
103+
package com.iluwatar.abstractEntity {
104+
abstract class PassengerVehicle {
105+
- noOfPassengers : int
106+
+ PassengerVehicle()
107+
+ PassengerVehicle(manufacturer : String, model : String, noOfPassengers : int)
108+
# canEqual(other : Object) : boolean
109+
+ equals(o : Object) : boolean
110+
+ getNoOfPassengers() : int
111+
+ hashCode() : int
112+
+ setNoOfPassengers(noOfPassengers : int)
113+
+ toString() : String
114+
}
115+
abstract class TransportVehicle {
116+
- loadCapacity : int
117+
+ TransportVehicle()
118+
+ TransportVehicle(manufacturer : String, model : String, loadCapacity : int)
119+
# canEqual(other : Object) : boolean
120+
+ equals(o : Object) : boolean
121+
+ getLoadCapacity() : int
122+
+ hashCode() : int
123+
+ setLoadCapacity(loadCapacity : int)
124+
+ toString() : String
125+
}
126+
abstract class Vehicle {
127+
- manufacturer : String
128+
- model : String
129+
- vehicleId : int
130+
+ Vehicle()
131+
+ Vehicle(manufacturer : String, model : String)
132+
+ Vehicle(vehicleId : int, manufacturer : String, model : String)
133+
# canEqual(other : Object) : boolean
134+
+ equals(o : Object) : boolean
135+
+ getManufacturer() : String
136+
+ getModel() : String
137+
+ getVehicleId() : int
138+
+ hashCode() : int
139+
+ setManufacturer(manufacturer : String)
140+
+ setModel(model : String)
141+
+ setVehicleId(vehicleId : int)
142+
+ toString() : String
143+
}
144+
}
145+
package com.iluwatar {
146+
class SingleTableInheritance {
147+
- vehicleService : VehicleService
148+
+ SingleTableInheritance(vehicleService : VehicleService)
149+
+ main(args : String[]) {static}
150+
+ run(args : String[])
151+
}
152+
}
153+
SingleTableInheritance --> "-vehicleService" VehicleService
154+
VehicleService --> "-vehicleRepository" VehicleRepository
155+
PassengerVehicle --|> Vehicle
156+
TransportVehicle --|> Vehicle
157+
Car --|> PassengerVehicle
158+
Freighter --|> TransportVehicle
159+
PassengerVehicle --|> Vehicle
160+
Train --|> PassengerVehicle
161+
TransportVehicle --|> Vehicle
162+
Truck --|> TransportVehicle
163+
@enduml

single-table-inheritance/pom.xml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<parent>
7+
<groupId>com.iluwatar</groupId>
8+
<artifactId>java-design-patterns</artifactId>
9+
<version>1.26.0-SNAPSHOT</version>
10+
</parent>
11+
12+
<artifactId>single-table-inheritance</artifactId>
13+
14+
<dependencies>
15+
<dependency>
16+
<groupId>org.springframework.boot</groupId>
17+
<artifactId>spring-boot-starter-data-jpa</artifactId>
18+
</dependency>
19+
<dependency>
20+
<groupId>com.h2database</groupId>
21+
<artifactId>h2</artifactId>
22+
<scope>runtime</scope>
23+
</dependency>
24+
<dependency>
25+
<groupId>org.projectlombok</groupId>
26+
<artifactId>lombok</artifactId>
27+
<optional>true</optional>
28+
</dependency>
29+
<dependency>
30+
<groupId>org.springframework.boot</groupId>
31+
<artifactId>spring-boot-starter-test</artifactId>
32+
<scope>test</scope>
33+
</dependency>
34+
</dependencies>
35+
36+
</project>

0 commit comments

Comments
 (0)