How to use Table per Class Hierarchy in JPA

In this tutorial, we will explore how to utilize JPA’s table per class hierarchy strategy to map a class hierarchy to a denormalized database table. The table per class hierarchy strategy allows multiple classes in a hierarchy to be combined into a single table, simplifying the database schema and reducing the number of joins required.

Adding a Discriminator Column

In order to use a table per class hierarchy strategy we need to add a discriminator column which identifies the type and the subclass. You can define a Discriminator Column through the Hibernate Configuration or using Annotations.

Defining the Discriminator Column in Hibernate

Let’s define the following Model Classes:

package com.sample
public class Vehicle {

    long id;
    int noOfTyres;
    private String colour;
   // Constructors and getters/setters
}
package com.sample
public class Car extends Vehicle {


    private String licensePlate;
    long price;
    int speed;
    // Constructors and getters/setters
}

Table per class hierarchy hibernate
And here’s the Database view which contains the DISCRIMINATOR field which will contain either “C” or “V” depending if you are inserting a new Car or a new Vehicle.
Table per class hierarchy hibernate
Finally, here is the hibernate configuration:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
 
<hibernate-mapping package="com.sample">
 
    <class name="Vehicle" table="VEHICLE" discriminator-value="V">
        <id name="id" column="VEHICLE_ID">
            <generator class="native" />
        </id>
 
        <discriminator column="DISCRIMINATOR" type="string" />
 
        <property name="noOfTyres" type="integer" column="NO_OF_TYRES" />
        <property name="colour" type="string"  />
 
        <subclass name="Car" extends="Vehicle" discriminator-value="C">
                <property name="licensePlate" column="LICENSE_PLATE" />
                <property name="price" type="long"   />
                <property name="speed" type="integer"   />
        </subclass>
    </class>
</hibernate-mapping>

Defining the Discriminator Column using JPA annotations

This is the same, using JPA Annotations:

import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;
 
 
@Entity
@Table(name = "VEHICLE")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(
    name="discriminator",
    discriminatorType=DiscriminatorType.STRING
)
@DiscriminatorValue(value="V")
public class Vehicle {
 
    @Id
    @GeneratedValue
    @Column(name = "VEHICLE_ID")
    private Long id;
     
    @Column(name="NO_OF_TYRES")
    int noOfTyres;
     
    @Column
    private String colour;
     
    // Constructors and Getter/Setter methods,
}
 
import javax.persistence.Column;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.Table;
 
@Entity
@Table(name="VEHICLE")
@DiscriminatorValue("C")
public class Car extends Vehicle {

    @Column(name="license_plate")
    private String licensePlate;

    @Column
    long price;

    @Column
    int speed;


    // Constructors and Getter/Setter methods,
}

The @DiscriminatorColumn annotation in JPA is used to specify the column that will store the discriminator value for entities that are part of an inheritance hierarchy. The discriminator value is used to differentiate between different subclasses within the hierarchy when storing and retrieving data from the database.

Here are the key aspects of the @DiscriminatorColumn annotation:

  • The @DiscriminatorColumn annotation is applied at the class level, typically on the parent entity class that defines the inheritance strategy.
  • It takes a single attribute name, which specifies the name of the column in the database that will store the discriminator value.
  • By default, the discriminator column is of type VARCHAR and has a length of 31 characters. You can customize the column type and length by using additional attributes such as columnDefinition and length.
  • The discriminator column is automatically created in the database schema when using JPA’s automatic schema generation feature.
  • The values in the discriminator column are automatically populated by the JPA provider based on the type of entity being persisted or retrieved.

Inserting Data in a Table that uses Table per Class Strategy

In the following example we will insert at first a Vehicle class (will into the VEHICLE table using the discriminator “V”), then we will insert a Car Object into the VEHICLE table (using the discriminator “C”)

 
        SessionFactory sf = HibernateUtil.getSessionFactory();
        Session session = sf.openSession();
        session.beginTransaction();
 
        // Will use discriminator V 
        Vehicle v = new Vehicle(1, 4, "red");
        session.save(v);
        // Will use discriminator C
        Car car = new Car("AB123456", 15000l, 200);
        session.save(c);
         
        session.getTransaction().commit();
        session.close();

Advantages and Disadvantages of using Table Per Class Strategy

The Table per Class strategy in JPA, where each class in a hierarchy is mapped to its own table, has both advantages and disadvantages. Let’s explore them:

Advantages of Table Per Class Strategy:

  1. Simplicity: The table per class strategy provides a straightforward mapping approach, as each class in the hierarchy corresponds to a separate table in the database. This mapping scheme is easy to understand and implement.
  2. Performance: In scenarios where you need to retrieve objects of a specific class quickly, the table per class strategy can be beneficial. Since each class has its own table, querying a specific class’s data does not require joins or filtering on a discriminator column, resulting in potentially better performance.
  3. Flexibility: The table per class strategy allows each class in the hierarchy to have its own set of columns. This provides flexibility in terms of adding, modifying, or removing columns specific to each class without impacting other classes.

Disadvantages of Table Per Class Strategy:

  1. Redundancy and Denormalization: The table per class strategy often results in redundant data and denormalization. Each table in the hierarchy contains its own set of columns, including inherited attributes, which can lead to duplication of data.
  2. Query Complexity: As the number of subclasses increases in the hierarchy, querying data that spans the entire hierarchy can become complex. Joining multiple tables can be cumbersome and may lead to more complex and slower queries compared to other strategies.
  3. Maintenance: The table per class strategy can be more challenging to maintain, especially when modifications to the class hierarchy are required.
  4. Indexing and Performance: Indexing the tables in the hierarchy for efficient querying can be challenging. Having separate tables for each class may require additional effort to design and optimize indexes properly, impacting overall performance.
  5. Schema Complexity: The database schema generated using the table per class strategy can become more complex and harder to comprehend compared to other strategies. The presence of multiple tables, possibly with duplicated columns, can make the schema less intuitive and harder to work with.
Found the article helpful? if so please follow us on Socials