-
-
Notifications
You must be signed in to change notification settings - Fork 26.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* 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
- Loading branch information
Showing
16 changed files
with
811 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
--- | ||
title: Single Table Inheritance Pattern | ||
category: Structural | ||
language: en | ||
tag: | ||
- Data access | ||
--- | ||
|
||
## Single Table Inheritance(STI) | ||
|
||
## Intent | ||
|
||
Represents an inheritance hierarchy of classes as a single table that has columns for all the fields of the various classes. | ||
|
||
## Explanation | ||
|
||
Real-world example | ||
|
||
> There can be many different types of vehicles in this world but all of them | ||
> come under the single umbrella of Vehicle | ||
In plain words | ||
|
||
> It maps each instance of class in an inheritance tree into a single table. | ||
Wikipedia says | ||
|
||
> Single table inheritance is a way to emulate object-oriented inheritance in a relational database. | ||
> When mapping from a database table to an object in an object-oriented language, | ||
> a field in the database identifies what class in the hierarchy the object belongs to. | ||
> All fields of all the classes are stored in the same table, hence the name "Single Table Inheritance". | ||
**Programmatic Example** | ||
|
||
Baeldung - Hibernate Inheritance | ||
|
||
> We can define the strategy we want to use by adding the @Inheritance annotation to the superclass: | ||
```java | ||
@Entity | ||
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) | ||
public class MyProduct { | ||
@Id | ||
private long productId; | ||
private String name; | ||
|
||
// constructor, getters, setters | ||
} | ||
``` | ||
|
||
The identifier of the entities is also defined in the superclass. | ||
|
||
Then we can add the subclass entities: | ||
|
||
```java | ||
@Entity | ||
public class Book extends MyProduct { | ||
private String author; | ||
} | ||
``` | ||
|
||
```java | ||
@Entity | ||
public class Pen extends MyProduct { | ||
private String color; | ||
} | ||
``` | ||
Discriminator Values | ||
|
||
- Since the records for all entities will be in the same table, Hibernate needs a way to differentiate between them. | ||
|
||
- By default, this is done through a discriminator column called DTYPE that has the name of the entity as a value. | ||
|
||
- To customize the discriminator column, we can use the @DiscriminatorColumn annotation: | ||
|
||
```java | ||
@Entity(name="products") | ||
@Inheritance(strategy = InheritanceType.SINGLE_TABLE) | ||
@DiscriminatorColumn(name="product_type", | ||
discriminatorType = DiscriminatorType.INTEGER) | ||
public class MyProduct { | ||
// ... | ||
} | ||
``` | ||
- Here we’ve chosen to differentiate MyProduct subclass entities by an integer column called product_type. | ||
|
||
- Next, we need to tell Hibernate what value each subclass record will have for the product_type column: | ||
|
||
```java | ||
@Entity | ||
@DiscriminatorValue("1") | ||
public class Book extends MyProduct { | ||
// ... | ||
} | ||
``` | ||
```java | ||
@Entity | ||
@DiscriminatorValue("2") | ||
public class Pen extends MyProduct { | ||
// ... | ||
} | ||
``` | ||
|
||
- Hibernate adds two other predefined values that the annotation can take — null and not null: | ||
|
||
- @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. | ||
- @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. | ||
|
||
|
||
## Class diagram | ||
|
||
![alt text](./etc/single-table-inheritance.urm.png "Singleton pattern class diagram") | ||
|
||
## Applicability | ||
|
||
Use the Singleton pattern when | ||
|
||
* Use STI When The Subclasses Have The Same Fields/Columns But Different Behavior | ||
- 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. | ||
<br><br> | ||
* Use STI When We Expect To Perform Queries Across All Subclasses | ||
- 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. | ||
|
||
|
||
### Tutorials | ||
|
||
- <a href ="https://www.youtube.com/watch?v=M5YrLtAHtOo" >Java Brains - Single Table Inheritance</a> | ||
|
||
## Consequences | ||
|
||
* Fields are sometimes relevant and sometimes not, which can be confusing | ||
to people using the tables directly. | ||
* Columns used only by some subclasses lead to wasted space in the database. | ||
How much this is actually a problem depends on the specific data | ||
characteristics and how well the database compresses empty columns. | ||
Oracle, for example, is very efficient in trimming wasted space, particularly | ||
if you keep your optional columns to the right side of the database | ||
table. Each database has its own tricks for this. | ||
* The single table may end up being too large, with many indexes and frequent | ||
locking, which may hurt performance. You can avoid this by having | ||
separate index tables that either list keys of rows that have a certain property | ||
or that copy a subset of fields relevant to an index. | ||
* You only have a single namespace for fields, so you have to be sure that | ||
you don’t use the same name for different fields. Compound names with | ||
the name of the class as a prefix or suffix help here. | ||
|
||
## Related patterns | ||
|
||
* MappedSuperclass | ||
* Single Table | ||
* Joined Table | ||
* Table per Class | ||
|
||
## Credits | ||
|
||
* [Single Table Inheritance - martinFowler.com](https://www.martinfowler.com/eaaCatalog/singleTableInheritance.html) | ||
* [Patterns of Enterprise Application Architecture](https://books.google.co.in/books?id=vqTfNFDzzdIC&pg=PA278&redir_esc=y#v=onepage&q&f=false) |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
163 changes: 163 additions & 0 deletions
163
single-table-inheritance/etc/single-table-inheritance.urm.puml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
@startuml | ||
package com.iluwatar.repository { | ||
interface VehicleRepository { | ||
} | ||
} | ||
package com.iluwatar.service { | ||
class VehicleService { | ||
- vehicleRepository : VehicleRepository | ||
+ VehicleService(vehicleRepository : VehicleRepository) | ||
+ deleteVehicle(vehicle : Vehicle) | ||
+ getAllVehicles() : List<Vehicle> | ||
+ getVehicle(vehicleId : int) : Vehicle | ||
+ saveVehicle(vehicle : Vehicle) : Vehicle | ||
+ updateVehicle(vehicle : Vehicle) : Vehicle | ||
} | ||
} | ||
package com.iluwatar.entity { | ||
class Car { | ||
- engineCapacity : int | ||
+ Car() | ||
+ Car(manufacturer : String, model : String, noOfPassengers : int, engineCapacity : int) | ||
# canEqual(other : Object) : boolean | ||
+ equals(o : Object) : boolean | ||
+ getEngineCapacity() : int | ||
+ hashCode() : int | ||
+ setEngineCapacity(engineCapacity : int) | ||
+ toString() : String | ||
} | ||
class Freighter { | ||
- flightLength : double | ||
+ Freighter() | ||
+ Freighter(manufacturer : String, model : String, countOfSeats : int, loadCapacity : int, flightLength : double) | ||
# canEqual(other : Object) : boolean | ||
+ equals(o : Object) : boolean | ||
+ getFlightLength() : double | ||
+ hashCode() : int | ||
+ setFlightLength(flightLength : double) | ||
+ toString() : String | ||
} | ||
abstract class PassengerVehicle { | ||
- noOfPassengers : int | ||
+ PassengerVehicle() | ||
+ PassengerVehicle(manufacturer : String, model : String, noOfPassengers : int) | ||
# canEqual(other : Object) : boolean | ||
+ equals(o : Object) : boolean | ||
+ getNoOfPassengers() : int | ||
+ hashCode() : int | ||
+ setNoOfPassengers(noOfPassengers : int) | ||
+ toString() : String | ||
} | ||
class Train { | ||
- noOfCarriages : int | ||
+ Train() | ||
+ Train(manufacturer : String, model : String, noOfPassengers : int, noOfCarriages : int) | ||
# canEqual(other : Object) : boolean | ||
+ equals(o : Object) : boolean | ||
+ getNoOfCarriages() : int | ||
+ hashCode() : int | ||
+ setNoOfCarriages(noOfCarriages : int) | ||
+ toString() : String | ||
} | ||
abstract class TransportVehicle { | ||
- loadCapacity : int | ||
+ TransportVehicle() | ||
+ TransportVehicle(manufacturer : String, model : String, loadCapacity : int) | ||
# canEqual(other : Object) : boolean | ||
+ equals(o : Object) : boolean | ||
+ getLoadCapacity() : int | ||
+ hashCode() : int | ||
+ setLoadCapacity(loadCapacity : int) | ||
+ toString() : String | ||
} | ||
class Truck { | ||
+ towingCapacity : int | ||
+ Truck() | ||
+ Truck(manufacturer : String, model : String, loadCapacity : int, towingCapacity : int) | ||
# canEqual(other : Object) : boolean | ||
+ equals(o : Object) : boolean | ||
+ getTowingCapacity() : int | ||
+ hashCode() : int | ||
+ setTowingCapacity(towingCapacity : int) | ||
+ toString() : String | ||
} | ||
abstract class Vehicle { | ||
- manufacturer : String | ||
- model : String | ||
- vehicleId : int | ||
+ Vehicle() | ||
+ Vehicle(manufacturer : String, model : String) | ||
+ Vehicle(vehicleId : int, manufacturer : String, model : String) | ||
# canEqual(other : Object) : boolean | ||
+ equals(o : Object) : boolean | ||
+ getManufacturer() : String | ||
+ getModel() : String | ||
+ getVehicleId() : int | ||
+ hashCode() : int | ||
+ setManufacturer(manufacturer : String) | ||
+ setModel(model : String) | ||
+ setVehicleId(vehicleId : int) | ||
+ toString() : String | ||
} | ||
} | ||
package com.iluwatar.abstractEntity { | ||
abstract class PassengerVehicle { | ||
- noOfPassengers : int | ||
+ PassengerVehicle() | ||
+ PassengerVehicle(manufacturer : String, model : String, noOfPassengers : int) | ||
# canEqual(other : Object) : boolean | ||
+ equals(o : Object) : boolean | ||
+ getNoOfPassengers() : int | ||
+ hashCode() : int | ||
+ setNoOfPassengers(noOfPassengers : int) | ||
+ toString() : String | ||
} | ||
abstract class TransportVehicle { | ||
- loadCapacity : int | ||
+ TransportVehicle() | ||
+ TransportVehicle(manufacturer : String, model : String, loadCapacity : int) | ||
# canEqual(other : Object) : boolean | ||
+ equals(o : Object) : boolean | ||
+ getLoadCapacity() : int | ||
+ hashCode() : int | ||
+ setLoadCapacity(loadCapacity : int) | ||
+ toString() : String | ||
} | ||
abstract class Vehicle { | ||
- manufacturer : String | ||
- model : String | ||
- vehicleId : int | ||
+ Vehicle() | ||
+ Vehicle(manufacturer : String, model : String) | ||
+ Vehicle(vehicleId : int, manufacturer : String, model : String) | ||
# canEqual(other : Object) : boolean | ||
+ equals(o : Object) : boolean | ||
+ getManufacturer() : String | ||
+ getModel() : String | ||
+ getVehicleId() : int | ||
+ hashCode() : int | ||
+ setManufacturer(manufacturer : String) | ||
+ setModel(model : String) | ||
+ setVehicleId(vehicleId : int) | ||
+ toString() : String | ||
} | ||
} | ||
package com.iluwatar { | ||
class SingleTableInheritance { | ||
- vehicleService : VehicleService | ||
+ SingleTableInheritance(vehicleService : VehicleService) | ||
+ main(args : String[]) {static} | ||
+ run(args : String[]) | ||
} | ||
} | ||
SingleTableInheritance --> "-vehicleService" VehicleService | ||
VehicleService --> "-vehicleRepository" VehicleRepository | ||
PassengerVehicle --|> Vehicle | ||
TransportVehicle --|> Vehicle | ||
Car --|> PassengerVehicle | ||
Freighter --|> TransportVehicle | ||
PassengerVehicle --|> Vehicle | ||
Train --|> PassengerVehicle | ||
TransportVehicle --|> Vehicle | ||
Truck --|> TransportVehicle | ||
@enduml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | ||
<modelVersion>4.0.0</modelVersion> | ||
<parent> | ||
<groupId>com.iluwatar</groupId> | ||
<artifactId>java-design-patterns</artifactId> | ||
<version>1.26.0-SNAPSHOT</version> | ||
</parent> | ||
|
||
<artifactId>single-table-inheritance</artifactId> | ||
|
||
<dependencies> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-data-jpa</artifactId> | ||
</dependency> | ||
<dependency> | ||
<groupId>com.h2database</groupId> | ||
<artifactId>h2</artifactId> | ||
<scope>runtime</scope> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.projectlombok</groupId> | ||
<artifactId>lombok</artifactId> | ||
<optional>true</optional> | ||
</dependency> | ||
<dependency> | ||
<groupId>org.springframework.boot</groupId> | ||
<artifactId>spring-boot-starter-test</artifactId> | ||
<scope>test</scope> | ||
</dependency> | ||
</dependencies> | ||
|
||
</project> |
Oops, something went wrong.