Single Table Inheritance Pattern


This pattern belongs to Object-Relational Structural Patterns Catalog and this Catalog belongs to Patterns of Enterprise Application Architecture.

Intent

Represents an inheritance hierarchy of classes as a single table that has columns for all the fields of the various classes.
Single Table Inheritance maps all fields of all classes of an inheritance structure into a single table.

Explanation

Relational databases don’t support inheritance, so when mapping from objects to databases we have to consider how to represent our nice inheritance structures in relational tables.
When mapping to a relational database, we try to minimize the joins that can quickly mount up when processing an inheritance structure in multiple tables.
Single Table Inheritance maps all fields of all classes of an inheritance structure into a single table.

From above diagram, Player is the parent abstract class with common fields name.
Footballer and Cricketer are also players so these class extends the Player class plus define their own fields.
Bowler player is a Cricketer so it extends Cricketer class plus define their own fields.
Please note that the table players contain all the columns from all the classes.

How It Works

In this inheritance mapping scheme, we have one table that contains all the data for all the classes in the inheritance hierarchy. Each class stores the data that are relevant to it in one table row. Any columns in the database that aren’t relevant are left empty.

When to Use It

Single Table Inheritance is one of the options for mapping the fields in an inheritance hierarchy to a relational database. The alternatives are Class Table Inheritance and Concrete Table Inheritance.

These are the strengths of Single Table Inheritance:

  • There’s only a single table to worry about on the database.
  • There are no joins in retrieving data.
  • Any refactoring that pushes fields up or down the hierarchy doesn’t require you to change the database.

The weaknesses of Single Table Inheritance are :

  • 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.
Remember that you don’t need to use one form of inheritance mapping for your whole hierarchy.

Implementation

Let's take a JPA Single Table Inheritance Example
It is obvious that
  • idVehicle and manufacturer columns correspond to Vehicle class and these fields are inherited by all subclasses.These columns will have values for all types of vehicles, i.e. CarTruck and Bike
  • loadCapacity  is field of class TransportationVehicle and will be inherited by Truck. So all trucks will have a value in row for this column.
  • noOfPassengers is field of class PassengerVehicle and will be inherited by Car and Bike. So all cars and bikes will have a value for this column.
  • noOfContainersnoOfDoors and saddleHeight will have values that are only specific to TruckCarand Bike respectively as these fields are unique to them.
  • There is an extra column called vehicle_type in table. This is used to identify the type of object in entity model. It will store values those are specific to classes. It is called as Discriminator column.

Discriminator Column:

It is used to identify the type of object in the database.

Let's create Vehicle model class.

@DiscriminatorColumn(name = "VEHICLE_TYPE")
public abstract class Vehicle {
//Vehicle class code
}
Let us directly move to jpa single table inheritance example and design java classes corresponding to object model and database shown in the diagram above.
import javax.persistence.DiscriminatorColumn;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;
import javax.persistence.TableGenerator;

@Entity
@Table(name = "VEHICLE")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "VEHICLE_TYPE")
public abstract class Vehicle {

 @TableGenerator(name = "VEHICLE_GEN", table = "ID_GEN", pkColumnName = "GEN_NAME", valueColumnName = "GEN_VAL", allocationSize = 1)
 @Id
 @GeneratedValue(strategy = GenerationType.TABLE, generator = "VEHICLE_GEN")
 private int idVehicle;
 private String manufacturer;

 public int getIdVehicle() {
  return idVehicle;
 }

 public void setIdVehicle(int idVehicle) {
  this.idVehicle = idVehicle;
 }

 public String getManufacturer() {
  return manufacturer;
 }

 public void setManufacturer(String manufacturer) {
  this.manufacturer = manufacturer;
 }

}
  •  Vehicle class is abstract but it is our root entity and inheritance starts from here, hence it needs to be annotated with @Inheritance.  @Inheritance(strategy = InheritanceType.SINGLE_TABLE)  specifies we are using single table strategy.
  • @DiscriminatorColumn(name = "VEHICLE_TYPE") specifies the name of discriminator column which stores the values to identify type of entity in database.
import javax.persistence.MappedSuperclass;

@MappedSuperclass
public abstract class TransportationVehicle extends Vehicle {

 private int loadCapacity;

 public int getLoadCapacity() {
  return loadCapacity;
 }

 public void setLoadCapacity(int loadCapacity) {
  this.loadCapacity = loadCapacity;
 }

}
import javax.persistence.MappedSuperclass;

@MappedSuperclass
public abstract class PassengerVehicle extends Vehicle {

 private int noOfpassengers;

 public int getNoOfpassengers() {
  return noOfpassengers;
 }

 public void setNoOfpassengers(int noOfpassengers) {
  this.noOfpassengers = noOfpassengers;
 }

}
You will notice @MappedSuperClass annotation here:
MappedSuperClass annotation:
  • Classes annotated with @MappedSuperClass are like abstract classes in java
  • They are non persistent.
  • They cannot be queried over and are not mapped to any database table.
  • They are only used to contribute state and behavior to entities those are inherited from them.
  • It is good practice to mark abstract classes as @MappedSuperClass in jpa inheritance.
  • In our case, annotating TransportationVehicle and PassengerVehicle as @MappedSuperClassenables TruckCar and Bike classes to hold their state.
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;

@Entity
@DiscriminatorValue(value = "Truck")
public class Truck extends TransportationVehicle{

 private int noOfContainers;

 public int getNoOfContainers() {
  return noOfContainers;
 }

 public void setNoOfContainers(int noOfContainers) {
  this.noOfContainers = noOfContainers;
 }

}
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;

@Entity
@DiscriminatorValue(value = "Bike")
public class Bike extends PassengerVehicle {

 private int saddleHeight;

 public int getSaddleHeight() {
  return saddleHeight;
 }

 public void setSaddleHeight(int saddleHeight) {
  this.saddleHeight = saddleHeight;
 }

}
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;

@Entity
@DiscriminatorValue(value = "Car")
public class Car extends PassengerVehicle {

 private int noOfDoors;

 public int getNoOfDoors() {
  return noOfDoors;
 }

 public void setNoOfDoors(int noOfDoors) {
  this.noOfDoors = noOfDoors;
 }

}
Notice the @DisciriminatorValue annotation in this jpa single table inheritance example.
Now let us test this jpa single table inheritance example:
persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
 xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
 <persistence-unit name="AdvancedMapping">

  <class>com.ramesh.jpa.entities.Vehicle</class>
  <class>com.ramesh.jpa.entities.Truck</class>
  <class>com.ramesh.jpa.entities.Bike</class>
  <class>com.ramesh.jpa.entities.Car</class>
  <class>com.ramesh.jpa.entities.PassengerVehicle</class>
  <class>com.ramesh.jpa.entities.TransportationVehicle</class>
  <properties>
   <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
   <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/yourDBName" />
   <property name="javax.persistence.jdbc.user" value="yourDBUserName" />
   <property name="javax.persistence.jdbc.password" value="userDBPassword" />
  </properties>

 </persistence-unit>
</persistence>
Let's code to insert entities
/* Create EntityManagerFactory */
EntityManagerFactory emf = Persistence
   .createEntityManagerFactory("AdvancedMapping");

/* Create EntityManager */
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
transaction.begin();

Bike cbr1000rr = new Bike();
cbr1000rr.setManufacturer("honda");
cbr1000rr.setNoOfpassengers(1);
cbr1000rr.setSaddleHeight(30);
em.persist(cbr1000rr);

Car avantador = new Car();
avantador.setManufacturer("lamborghini");
avantador.setNoOfDoors(2);
avantador.setNoOfpassengers(2);
em.persist(avantador);

Truck truck = new Truck();
truck.setLoadCapacity(100);
truck.setManufacturer("mercedes");
truck.setNoOfContainers(2);
em.persist(truck);

transaction.commit();

References


Comments