Hibernate Inheritance Mapping

This lesson covers the different strategies for mapping inheritance hierarchies to database tables in Hibernate. We will explore strategies like Table per Class Hierarchy, Table per Subclass, and Table per Concrete Class.


Table per Subclass Strategy in Hibernate

Explanation: Table per Subclass Strategy

The Table per Subclass strategy is a database inheritance mapping technique where each concrete subclass in an inheritance hierarchy is mapped to its own table in the database. The table for each subclass contains columns for the subclass-specific properties as well as a foreign key referencing the primary key of the base class table. This creates a separate table for each type of entity, allowing for specific columns for each subclass without polluting the base class table.

Detailed Explanation

In this strategy, the base class has its own table, and each subclass has its own table as well. The subclass tables store only the properties specific to that subclass, and they have a foreign key that refers to the primary key of the base class table. When querying for objects of the base class, Hibernate needs to perform a JOIN operation with each of the subclass tables to retrieve all instances.

Advantages:

  • Clear separation of subclass-specific attributes into their own tables.
  • No NULL columns in the base table for subclass-specific properties.
  • Better performance for queries involving only subclass-specific properties.

Disadvantages:

  • More complex SQL queries when retrieving objects of the base class, requiring JOIN operations across multiple tables.
  • More complex database schema due to the multiple tables for each subclass.
  • Potential performance overhead when querying for all objects of the base class due to the required JOINs.

Implementation using Hibernate

We will demonstrate the Table per Subclass strategy using Hibernate annotations. Let's consider a scenario with a base class Vehicle and two subclasses: Car and Truck.

1. Base Class (Vehicle)

 import javax.persistence.*;

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@Table(name = "vehicle")
public class Vehicle {

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

    private String model;

    public Vehicle() {}

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

    public Long getId() {
        return id;
    }

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

    public String getModel() {
        return model;
    }

    public void setModel(String model) {
        this.model = model;
    }
} 

Explanation:

  • @Entity: Marks the class as a persistent entity.
  • @Table(name = "vehicle"): Specifies the table name in the database.
  • @Inheritance(strategy = InheritanceType.JOINED): Specifies the inheritance strategy as "Table per Subclass". JOINED means that each subclass will have its own table joined to the superclass table.
  • @Id: Marks the id field as the primary key.
  • @GeneratedValue(strategy = GenerationType.IDENTITY): Configures the primary key to be auto-generated by the database using an identity column.

2. Subclass (Car)

 import javax.persistence.*;

@Entity
@Table(name = "car")
@PrimaryKeyJoinColumn(name = "vehicle_id")
public class Car extends Vehicle {

    private int numberOfDoors;

    public Car() {}

    public Car(String model, int numberOfDoors) {
        super(model);
        this.numberOfDoors = numberOfDoors;
    }


    public int getNumberOfDoors() {
        return numberOfDoors;
    }

    public void setNumberOfDoors(int numberOfDoors) {
        this.numberOfDoors = numberOfDoors;
    }
} 

Explanation:

  • @Entity: Marks the class as a persistent entity.
  • @Table(name = "car"): Specifies the table name for the Car class.
  • @PrimaryKeyJoinColumn(name = "vehicle_id"): Specifies the foreign key column in the car table that references the primary key of the vehicle table. This establishes the relationship between the tables. The vehicle_id column in the `car` table will be a foreign key to the `id` column in the `vehicle` table.

3. Subclass (Truck)

 import javax.persistence.*;

@Entity
@Table(name = "truck")
@PrimaryKeyJoinColumn(name = "vehicle_id")
public class Truck extends Vehicle {

    private double loadCapacity;

    public Truck() {}

    public Truck(String model, double loadCapacity) {
        super(model);
        this.loadCapacity = loadCapacity;
    }

    public double getLoadCapacity() {
        return loadCapacity;
    }

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

Explanation:

  • @Entity: Marks the class as a persistent entity.
  • @Table(name = "truck"): Specifies the table name for the Truck class.
  • @PrimaryKeyJoinColumn(name = "vehicle_id"): Specifies the foreign key column in the truck table that references the primary key of the vehicle table.

4. Hibernate Configuration (hibernate.cfg.xml)

 <hibernate-configuration>
    <session-factory>
        <property name="connection.driver_class">com.mysql.cj.jdbc.Driver</property>
        <property name="connection.url">jdbc:mysql://localhost:3306/your_database</property>
        <property name="connection.username">your_username</property>
        <property name="connection.password">your_password</property>
        <property name="dialect">org.hibernate.dialect.MySQL8Dialect</property>
        <property name="hbm2ddl.auto">update</property>  <!-- Use 'create' for initial schema creation -->
        <property name="show_sql">true</property> <!-- Optional: Display SQL queries -->
        <property name="format_sql">true</property><!-- Optional: Format SQL queries -->

        <mapping class="Vehicle"/>
        <mapping class="Car"/>
        <mapping class="Truck"/>

    </session-factory>
</hibernate-configuration> 

Explanation:

  • Replace your_database, your_username, and your_password with your actual database credentials.
  • The <mapping class="..."/> elements tell Hibernate about the entity classes.
  • The hbm2ddl.auto property is set to update. This will automatically create the database tables based on the entity mappings if they don't exist, or update them if they do. Use `create` to drop and recreate the schema each time the application runs.

5. Example Usage (Java Code)

 import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;

public class HibernateTablePerSubclassExample {

    public static void main(String[] args) {
        Configuration configuration = new Configuration().configure("hibernate.cfg.xml");
        SessionFactory sessionFactory = configuration.buildSessionFactory();

        try (Session session = sessionFactory.openSession()) {
            Transaction transaction = session.beginTransaction();

            // Create and save a Vehicle
            Vehicle vehicle = new Vehicle("Generic Model");
            session.save(vehicle);

            // Create and save a Car
            Car car = new Car("Sedan", 4);
            session.save(car);

            // Create and save a Truck
            Truck truck = new Truck("Heavy Duty", 5000.0);
            session.save(truck);

            transaction.commit();
        } catch (Exception e) {
            e.printStackTrace();
        }

        try (Session session = sessionFactory.openSession()) {
            // Query all Vehicles
            System.out.println("All Vehicles:");
            session.createQuery("from Vehicle", Vehicle.class).list().forEach(v -> System.out.println(v.getClass().getSimpleName() + ": " + v.getModel()));

            // Query all Cars
            System.out.println("\nAll Cars:");
            session.createQuery("from Car", Car.class).list().forEach(c -> System.out.println(c.getModel() + ", Doors: " + c.getNumberOfDoors()));

            // Query all Trucks
            System.out.println("\nAll Trucks:");
            session.createQuery("from Truck", Truck.class).list().forEach(t -> System.out.println(t.getModel() + ", Capacity: " + t.getLoadCapacity()));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            sessionFactory.close();
        }
    }
} 

Explanation:

  • The code creates a Hibernate SessionFactory from the configuration file.
  • It then creates a Session and a Transaction.
  • Instances of Vehicle, Car, and Truck are created and saved to the database.
  • The transaction is committed.
  • Finally, the code demonstrates querying for all vehicles, all cars, and all trucks, printing their details to the console.