Hibernate Inheritance: Table Per Class hierarchy

Hi Guys, Here i am going to discuss some of the best features of Hibernate framework. For the next few classes i am going to discuss about inheritance in Java and how to implement it in Hibernate.

Object oriented systems are majorly implemented with “is a” and “has a” relationship. Relational databases supports only “has a” relationship between two entities. Hibernate can help you map such Objects with relational tables, only thing is we need to choose certain mapping strategy based on our needs.

Table Per Class Hierarchy:
Let us consider we have a Person class and Employee class which extend from Person class

Class Person {
property firstName
property lastName
}


Class Employee extends Person{
property joiningDate
property departmentName
}

In Table per Class Hierarchy , we store all the class hierarchy in a single table. A discriminator is a key to uniquely identify the base type of the class hierarchy.i.e we store the Employee and Person related information in Person table in database.

Following are the advantages and disadvantages of Table per Class Hierarchy.
Advantage
This hierarchy offers the best performance even for in the deep hierarchy since single select may suffice.
Disadvantage
The columns for properties declared by subclasses must be declared to accept null Values, these columns cannot be declared with NOT NULL constraint.

Following is the Sql script:

DROP TABLE IF EXISTS `test`.`person`;
CREATE TABLE  `test`.`person` (
  `person_id` bigint(10) NOT NULL AUTO_INCREMENT,
  `firstname` varchar(50) DEFAULT NULL,
  `lastname` varchar(50) DEFAULT NULL,
  `joining_date` date DEFAULT NULL,
  `department_name` varchar(50) DEFAULT NULL,
  `discriminator` varchar(20) NOT NULL,
  PRIMARY KEY (`person_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

In the following example i’ll demonstrate the mapping using hbm.xml file and next by annotation.
Person.Java

/**
 * 
 */
package com.spark.hibernate.model;

import java.io.Serializable;

/**
 * @author Sony
 * 
 */
public class Person implements Serializable {

	private int personId;
	private String firstName;
	private String lastName;
	
	public Person() {
		// TODO Auto-generated constructor stub
	}
	
	public Person(String firstName,String lastName) {
		this.firstName = firstName;
		this.lastName = lastName;
	}

	/**
	 * @return the personId
	 */
	public int getPersonId() {
		return personId;
	}

	/**
	 * @param personId
	 *            the personId to set
	 */
	public void setPersonId(int personId) {
		this.personId = personId;
	}

	/**
	 * @return the firstName
	 */
	public String getFirstName() {
		return firstName;
	}

	/**
	 * @param firstName
	 *            the firstName to set
	 */
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	/**
	 * @return the lastName
	 */
	public String getLastName() {
		return lastName;
	}

	/**
	 * @param lastName
	 *            the lastName to set
	 */
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

}

Employee.java

/**
 * 
 */
package com.spark.hibernate.model;

import java.io.Serializable;
import java.util.Date;

/**
 * @author Sony
 * 
 */
public class Employee extends Person implements Serializable {

	private Date joiningDate;
	private String departmentName;
	
	public Employee() {
		// TODO Auto-generated constructor stub
	}
	
	public Employee(String firstName,String lastName,Date joiningDate,String departmentName) {
		super(firstName,lastName);
		this.joiningDate = joiningDate;
		this.departmentName = departmentName;
	}

	/**
	 * @return the joiningDate
	 */
	public Date getJoiningDate() {
		return joiningDate;
	}

	/**
	 * @param joiningDate
	 *            the joiningDate to set
	 */
	public void setJoiningDate(Date joiningDate) {
		this.joiningDate = joiningDate;
	}

	/**
	 * @return the departmentName
	 */
	public String getDepartmentName() {
		return departmentName;
	}

	/**
	 * @param departmentName
	 *            the departmentName to set
	 */
	public void setDepartmentName(String departmentName) {
		this.departmentName = departmentName;
	}

}

Now lets look at hibernate.cfg.xml which allows the configuration for hibernate framework.

<?xml version="1.0" encoding="UTF-8"?>
<!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.connection.driver_class">com.mysql.jdbc.Driver</property>
  <property name="hibernate.connection.password">Password</property>
  <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/test</property>
  <property name="hibernate.connection.username">root</property>
  <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
  <property name="hibernate.query.startup_check">true</property>
  <property name="hibernate.show_sql">true</property>
  <property name="hibernate.use_sql_comments">true</property>
  <property name="hibernate.format_sql">true</property>
  <mapping resource="com/spark/hibernate/model/Person.hbm.xml"/>
 </session-factory>
</hibernate-configuration>

Now lets have a look at hbm file i name it as Person.hbm.xml

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 15 Jun, 2013 7:42:12 PM by Hibernate Tools 3.4.0.CR1 -->

<hibernate-mapping package="com.spark.hibernate.model">
    <class name="Person" table="PERSON" discriminator-value="P">
        <id name="personId" type="int">
            <column name="PERSON_ID" />
            <generator class="native" />
        </id>
        
        <discriminator column="DISCRIMINATOR" type="string" />
        <property name="firstName" type="java.lang.String">
            <column name="FIRSTNAME" />
        </property>
        <property name="lastName" type="java.lang.String">
            <column name="LASTNAME" />
        </property>
        
        <subclass name="Employee" extends="Person" discriminator-value="E">
        	<property name="joiningDate" type="date" column="joining_date"></property>
        	<property name="departmentName" type="string" column="department_name"></property>
        </subclass>
    </class>
</hibernate-mapping>

Lets have a small discussion on the above file, Note that we have only one hibernate mapping (hbm) file Person.hbm.xml. Both Person and Employee model(POJO) classes are defined within one hbm file.

tag is used to define the discriminator column.
NOTE: your discriminator tag should appear right after the id tag if not you will run into errors

tag is used to map the subclass Employee. Note that we have not used the usual tag to map Employee as it falls below in the hierarchy tree.

The discriminator-value for Person is defined as “P” and that for Employee is defined “E”, Thus, when Hibernate will persist the data for person or employee it will accordingly populate this value.

Lets have a look at our main class to run:
Main.java

/**
 * 
 */
package com.spark.hibernate.main;

import java.util.Date;

import org.hibernate.Session;
import org.hibernate.SessionFactory;

import com.spark.hibernate.model.Employee;
import com.spark.hibernate.model.Person;
import com.spark.hibernate.util.HibernateUtil;

/**
 * @author Sony
 *
 */
public class Main {

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
		Session session = sessionFactory.openSession();
		
		session.beginTransaction();
		
		Person person = new Person("James","Gosling");
		session.save(person);
		
		Employee employee = new Employee("Bill","Gates",new Date(), "Java Development");
		session.save(employee);
		
		session.getTransaction().commit();
		session.close();
		sessionFactory.close();
		
	}

}

We used a helper class to get the SessionFactory Object of Hibernate, so here it is:

/**
 * 
 */
package com.spark.hibernate.util;

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

/**
 * @author Sony
 *
 */
public class HibernateUtil {

	private static SessionFactory sessionFactory;
	
	public static SessionFactory getSessionFactory(){
		
		sessionFactory = new Configuration().configure().buildSessionFactory();
		return sessionFactory;
	}
}

Finally the output should look like the following

Table_Per_Class_Query_OP

Table_Per_Class

Happy Coding 🙂

Advertisements