Hibernate Inheritance Mapping

Hibernate provides several strategies to map an inheritance hierarchy of classes to a relational database. These strategies include:
  1. Single Table Inheritance (Single Table per Class Hierarchy)
  2. Table Per Class (Concrete Table Inheritance)
  3. Joined Table Inheritance

In this tutorial, we will explore each of these inheritance mapping strategies using examples.

Prerequisites

Before we start, ensure you have the following:

  • Java Development Kit (JDK) installed
  • Apache Maven installed
  • MySQL database installed and running (or any other relational database)
  • An IDE (such as IntelliJ IDEA, Eclipse, or VS Code) installed

Step 1: Setting Up the Hibernate Project

1.1 Create a Maven Project

  1. Open your IDE and create a new Maven project.

  2. Configure the pom.xml file:

Add the following dependencies to your pom.xml:

<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>

    <groupId>com.example</groupId>
    <artifactId>hibernate-inheritance-example</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.hibernate.orm</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>6.4.0.Final</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate.orm</groupId>
            <artifactId>hibernate-hikaricp</artifactId>
            <version>6.4.0.Final</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.26</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.32</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.7.32</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>17</source>
                    <target>17</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

1.2 Configure Hibernate

Create a file named hibernate.cfg.xml in the src/main/resources directory with the following content:

<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="hibernate.connection.driver_class">com.mysql.cj.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/your_database_name</property>
        <property name="hibernate.connection.username">your_username</property>
        <property name="hibernate.connection.password">your_password</property>
        <property name="hibernate.hbm2ddl.auto">update</property>
        <property name="hibernate.show_sql">true</property>
        <property name="hibernate.format_sql">true</property>
    </session-factory>
</hibernate-configuration>

Replace your_database_name, your_username, and your_password with your MySQL database credentials.

Step 2: Implementing Inheritance Strategies

2.1 Single Table Inheritance

In the Single-Table Inheritance strategy, a single table stores all the fields of the entire class hierarchy. Discriminator columns determine the type of each row.

2.1.1 Create Base Class and Subclasses

Create an Animal class in the com.example.hibernateinheritance.model package:

package com.example.hibernateinheritance.model;

import jakarta.persistence.*;

@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "animal_type", discriminatorType = DiscriminatorType.STRING)
public abstract class Animal {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    // Constructors, Getters, and Setters

    public Animal() {}

    public Animal(String name) {
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Create a Dog class that extends Animal:

package com.example.hibernateinheritance.model;

import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Entity;

@Entity
@DiscriminatorValue("Dog")
public class Dog extends Animal {

    private String breed;

    // Constructors, Getters, and Setters

    public Dog() {}

    public Dog(String name, String breed) {
        super(name);
        this.breed = breed;
    }

    public String getBreed() {
        return breed;
    }

    public void setBreed(String breed) {
        this.breed = breed;
    }
}

Create a Cat class that extends Animal:

package com.example.hibernateinheritance.model;

import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Entity;

@Entity
@DiscriminatorValue("Cat")
public class Cat extends Animal {

    private String color;

    // Constructors, Getters, and Setters

    public Cat() {}

    public Cat(String name, String color) {
        super(name);
        this.color = color;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }
}

2.2 Table Per Class

In the Table Per Class strategy, each class in the hierarchy is mapped to its own table. This means that no table contains the entire hierarchy.

2.2.1 Create Base Class and Subclasses

Create a Vehicle class in the com.example.hibernateinheritance.model package:

package com.example.hibernateinheritance.model;

import jakarta.persistence.*;

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Vehicle {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String manufacturer;

    // Constructors, Getters, and Setters

    public Vehicle() {}

    public Vehicle(String manufacturer) {
        this.manufacturer = manufacturer;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getManufacturer() {
        return manufacturer;
    }

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

Create a Car class that extends Vehicle:

package com.example.hibernateinheritance.model;

import jakarta.persistence.Entity;

@Entity
public class Car extends Vehicle {

    private int doors;

    // Constructors, Getters, and Setters

    public Car() {}

    public Car(String manufacturer, int doors) {
        super(manufacturer);
        this.doors = doors;
    }

    public int getDoors() {
        return doors;
    }

    public void setDoors(int doors) {
        this.doors = doors;
    }
}

Create a Bike class that extends Vehicle:

package com.example.hibernateinheritance.model;

import jakarta.persistence.Entity;

@Entity
public class Bike extends Vehicle {

    private boolean hasCarrier;

    // Constructors, Getters, and Setters

    public Bike() {}

    public Bike(String manufacturer, boolean hasCarrier) {
        super(manufacturer);
        this.hasCarrier = hasCarrier;
    }

    public boolean isHasCarrier() {
        return hasCarrier;
    }

    public void setHasCarrier(boolean hasCarrier) {
        this.hasCarrier = hasCarrier;
    }
}

2.3 Joined Table Inheritance

In the Joined Table Inheritance strategy, each class in the hierarchy is mapped to its own table, which is joined using a foreign key.

2.3.1 Create Base Class and Subclasses

Create a Person class in the com.example.hibernateinheritance.model package:

package com.example.hibernateinheritance.model;

import jakarta.persistence.*;

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class Person {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    // Constructors, Getters, and Setters

    public Person() {}

    public Person(String name) {
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Create a Student class that extends Person:

package com.example.hibernateinheritance.model;

import jakarta.persistence.Entity;

@Entity
public class Student extends Person {

    private String course;

    // Constructors, Getters, and Setters

    public Student() {}

    public Student(String name, String course) {
        super(name);
        this.course = course;
    }

    public String getCourse() {
        return course;
    }

    public void setCourse(String course) {
        this.course = course;
    }
}

Create a Teacher class that extends Person:

package com.example.hibernateinheritance.model;

import jakarta.persistence.Entity;

@Entity
public class Teacher extends Person {

    private String subject;

    // Constructors, Getters, and Setters

    public Teacher() {}

    public Teacher(String name, String subject) {
        super(name);
        this.subject = subject;
    }

    public String getSubject() {
        return subject;
    }

    public void setSubject(String subject) {
        this.subject = subject;
    }
}

Step 3: Creating the Hibernate Utility Class

Create a HibernateUtil class in the com.example.hibernateinheritance.util package:

package com.example.hibernateinheritance.util;

import com.example.hibernateinheritance.model.*;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;

public class HibernateUtil {

    private static final SessionFactory sessionFactory = buildSessionFactory();

    private static SessionFactory buildSessionFactory() {
        try {
            Configuration configuration = new Configuration();
            configuration.configure("hibernate.cfg.xml");
            configuration.addAnnotatedClass(Animal.class);
            configuration.addAnnotatedClass(Dog.class);
            configuration.addAnnotatedClass(Cat.class);
            configuration.addAnnotatedClass(Vehicle.class);
            configuration.addAnnotatedClass(Car.class);
            configuration.addAnnotatedClass(Bike.class);
            configuration.addAnnotatedClass(Person.class);
            configuration.addAnnotatedClass(Student.class);
            configuration.addAnnotatedClass(Teacher.class);
            return configuration.buildSessionFactory(new StandardServiceRegistryBuilder()
                    .applySettings(configuration.getProperties()).build());
        } catch (Throwable ex) {
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    public static void shutdown() {
        getSessionFactory().close();
    }
}

Step 4: Demonstrating Inheritance Mappings

4.1 Create a Main Class

Create a Main class in the com.example.hibernateinheritance package to demonstrate the inheritance mappings:

package com.example.hibernateinheritance;

import com.example.hibernateinheritance.model.*;
import com.example.hibernateinheritance.util.HibernateUtil;
import org.hibernate.Session;
import org.hibernate.Transaction;

public class Main {
    public static void main(String[] args) {
        Session session = HibernateUtil.getSessionFactory().openSession();
        Transaction transaction = session.beginTransaction();

        // Single Table Inheritance
        Animal dog = new Dog("Buddy", "Golden Retriever");
        Animal cat = new Cat("Kitty", "Black");
        session.save(dog);
        session.save(cat);

        // Table Per Class
        Vehicle car = new Car("Toyota", 4);
        Vehicle bike = new Bike("Yamaha", true);
        session.save(car);
        session.save(bike);

        // Joined Table Inheritance
        Person student = new Student("John", "Computer Science");
        Person teacher = new Teacher("Ms. Smith", "Mathematics");
        session.save(student);
        session.save(teacher);

        transaction.commit();
        session.close();

        HibernateUtil.shutdown();
    }
}

Conclusion

In this tutorial, we explored Hibernate's three main inheritance mapping strategies: Single Table Inheritance, Table Per Class, and Joined Table Inheritance. We demonstrated how to configure each strategy and provided examples for each. Understanding these strategies is crucial for designing efficient and maintainable data models in applications that involve complex inheritance hierarchies.

Comments