Hibernate - Table per Hiearchy



Introduction

In Hibernate, Java classes are mapped to DB tables. Suppose we have a class and it has several sub-classes, there are three ways Hibernate provides to map the classes to tables −

  • Table per Hierarchy

  • Table per Concrete Class

  • Table per Subclass

Let's discuss Table per hierarchy in details with an example.

Table per Hierarchy

Let's say we have a class 'Shape', and it has two subclasses, 'Circle' and 'Rectangle'. In this type of mapping, we will have only one table in the database.

Class Hierachy

There are three classes in the hierarchy. Shape is the superclass and Circle and Rectangle are the child classes. Consider the type variable. Its value is shape for Shape class, circle for Circle class and rectangle for Rectangle class. This is for us to know from database what type of Object is stored. Thus, it is the discriminator.

Create Mapping Classes

Let's create the POJO classes whose data is to be persisted in the database.

Shape.java

package com.tutorialspoint;

public class Shape {
   public int Id;
   public String type;
   public double area;

   public Shape() {
      this.type = "Shape";
   }

   public double calculateArea() {
      return 0; // default implementation. 
   }

   public int getId() {
      return Id;
   }

   public void setId(int i) {
      this.Id = i;
   }

   public String getType() {
      return this.type;
   }
   
   public void setArea(double a) {
      this.area = a;
   }

   public double getArea() {
      return this.area;
   }
}

Circle.java

package com.tutorialspoint;

import java.math.*;

public class Circle extends Shape {

   private double radius;

   public Circle(double rad ) {
      this.radius = rad;
      this.type = "Circle";
   }

   @Override
   public double calculateArea() {
      area =  Math.PI * radius * radius;
      return area;
   }
}

Rectangle.java

package com.tutorialspoint;

public class Rectangle extends Shape {

   private double length, width;

   public Rectangle(double length, double width) {
      this.length = length;
      this.width = width;
      this.type = "Rectangle";
   }

   @Override
   public double calculateArea() {
      return length * width;
   }
}

Create Database Table

Let us create a table in the database. There would be one table corresponding to above objects, you are willing to provide persistence. Consider above objects need to be stored and retrieved into the following RDBMS table −

CREATE TABLE students.shape( 
   id int NOT NULL, 
   type VARCHAR(20), 
   radius int, 
   length int,
   width int,
   area float
);

Create Mapping Configuration File

Now create a mapping file that instructs Hibernate how to map the defined classes to the database table.

shape.hbm.xml

<?xml version='1.0' encoding='UTF-8'?>  
<!DOCTYPE hibernate-mapping PUBLIC  
   "-//Hibernate/Hibernate Mapping DTD 5.3//EN"  
   "http://hibernate.org/dtd/hibernate-mapping-3.0.dtd">  
<hibernate-mapping>  
   <class name="com.tutorialspoint.Shape" table="shape" discriminator-value="Shape">  
      <id name="id">  
         <generator class="increment"></generator>  
      </id>  
      <discriminator column="type" type="string"></discriminator>  
      <property name="area"></property>  
      <subclass name="com.tutorialspoint.Circle" discriminator-value="Circle">  
         <property name="radius"></property>  
      </subclass>  
      <subclass name="com.tutorialspoint.Rectangle" discriminator-value="Rectangle">  
         <property name="width"></property>  
         <property name="length"></property>  
      </subclass>  
   </class>  
</hibernate-mapping>

Create Hibernate Configuration File

Now create a hibernate configuration file for database and other details.

hibernate.cfg.xml

<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM 
   "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
   <session-factory>
      <property name="hbm2ddl.auto">update</property>
      <property name = "hibernate.dialect">
         org.hibernate.dialect.MySQL8Dialect
      </property>
      <property name = "hibernate.connection.driver_class">
         com.mysql.cj.jdbc.Driver
      </property>
      <!'students' is the database name -->
      <property name = "hibernate.connection.url">
         jdbc:mysql://localhost/students
      </property>
      <property name = "hibernate.connection.username">
         root
      </property>
      <property name = "hibernate.connection.password">
         guest123
      </property>
      <!-- List of XML mapping files -->
      <mapping resource = "shape.hbm.xml"/>
   </session-factory>
</hibernate-configuration>

Create Application Class

Finally, we will create our application class with the main() method to run the application. We will use this application to test Table per Hiearchy mapping.

TestTablePerhierarchy.java

package com.tutorialspoint;

import org.hibernate.Session;  
import org.hibernate.SessionFactory;  
import org.hibernate.Transaction;  
import org.hibernate.boot.Metadata;  
import org.hibernate.boot.MetadataSources;  
import org.hibernate.boot.registry.StandardServiceRegistry;  
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;  

public class TestTablePerHierarchy {    
   public static void main(String[] args) {    
      // create a hibernate configuration 
      StandardServiceRegistry ssr=new StandardServiceRegistryBuilder().configure("hibernate.cfg.xml").build();  
      Metadata meta=new MetadataSources(ssr).getMetadataBuilder().build();  
      // get the sessionfactory and open the session
      SessionFactory factory=meta.getSessionFactoryBuilder().build();  
      Session session=factory.openSession();  

      // begin the transaction
      Transaction t=session.beginTransaction();    

      // create Shape instance and set details to persist
      Shape s1=new Shape();    
      s1.setType(s1.getType());
      s1.setArea(s1.calculateArea());

      // create Circle instance and set details to persist
      Circle c1=new Circle(2);    
      c1.setType(c1.getType());
      c1.setArea(c1.calculateArea());  

      // create Rectangle instance and set details to persist
      Rectangle r1 = new Rectangle(3,4);    
      r1.setType(r1.getType());
      r1.setArea(r1.calculateArea());

      // persist all instances
      session.persist(s1);    
      session.persist(c1);    
      session.persist(r1);    

      // commit the transaction and close the session
      t.commit();    
      session.close();    
      System.out.println(" Successfully persisted 3 classes. Please check your database for results.");    
   } 
}

Compilation and Execution

Execute TestTablePerHierarchy binary to run the program.

Output

You would get the following result, and records would be created in the Shape table.

$java TestTablePerHierarchy
 Successfully persisted 3 classes. Please check your database for results.

If you check your Shape table, it should have the following records −

mysql> select * from shape;
+----+-----------+--------+--------+-------+-------+
| id | type      | radius | length | width | area  |
+----+-----------+--------+--------+-------+-------+
|  1 | Shape     |   NULL |   NULL |  NULL |     0 |
|  2 | Circle    |      2 |   NULL |  NULL | 12.56 |
|  3 | Rectangle |   NULL |      3 |     4 |    12 |
+----+-----------+--------+--------+-------+-------+
3 rows in set (0.00 sec)
Advertisements