Hibernate - Table per SubClass



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 SubClass in details with an example.

Table per SubClass

Let's say we have a class 'Shape', and it has two subclasses, 'Circle' and 'Rectangle'. In this strategy, we create a new table for each class. Also, in the subclass table, all fields of parent class are added. So, we don't need to join with superclass table, as the subclass table has all the fields of the superclass.

Class Hierachy

There are three classes in the hierarchy. Shape is the superclass and Circle and Rectangle are the child classes. To map the classes, we'll using joined-subclass element in *.hbm.xml as shown below:

<class name="com.tutorialspoint.Shape" table="shape">
...
      <joined-subclass name="com.tutorialspoint.Circle"
         table="circle">
		 <key column="sid"></key>
         <property name="radius"></property>
      </joined-subclass>
	  <joined-subclass name="com.tutorialspoint.Rectangle"
         table="rectangle">
		 <key column="rid"></key>
         <property name="length"></property>
         <property name="width"></property>
      </joined-subclass>
...

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() {
      this.area= 0;
      setArea(area);
      return area;
   }

   public int getId() {
      return Id;
   }

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

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

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;
   }
   public void setArea() {
      this.area = calculateArea();
   }
   public double getArea() {
      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;
   }
   public void setArea() {
      this.area = calculateArea();
   }
   public double getArea() {
      return area;
   }
}

Create Database Tables

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

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

CREATE TABLE students.circle( 
   sid int NOT NULL, 
   radius float
);

CREATE TABLE students.rectangle( 
   rid int NOT NULL,
   length float,
   width 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://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
   <class name="com.tutorialspoint.Shape" table="shape">
      <id name="id">
         <generator class="increment"></generator>
      </id>
      <property name="type"></property>
      <property name="area"></property>
      <joined-subclass name="com.tutorialspoint.Circle"
         table="circle">
		 <key column="sid"></key>
         <property name="radius"></property>
      </joined-subclass>
	  <joined-subclass name="com.tutorialspoint.Rectangle"
         table="rectangle">
		 <key column="rid"></key>
         <property name="length"></property>
         <property name="width"></property>
      </joined-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>

Having property hbm2ddlauto to update will create the table if not already created during program execution.

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 Concrete mapping.

TestTablePerSubclass.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 TestTablePerSubclass{ {    
   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(0);

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

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

      // 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 tables, those should have the following records −

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

mysql> select * from circle;
+-----+--------+
| sid | radius |
+-----+--------+
|   2 |      2 |
+-----+--------+
1 row in set (0.00 sec)

mysql> select * from rectangle;
+-----+--------+--------+
| rid | length | height |
+-----+--------+--------+
|   3 |      3 |      4 |
+-----+--------+--------+
1 row in set (0.00 sec)
Advertisements