Sunday, 6 October 2013

Hibernate Mapping Strategy : Table per class Hierachy

Table per class Hierachy :  As the name suggest, the entire class hierarchy (abstract, interface and concrete class) is mapped to a single table. This table includes columns for all the properties of all classes in the hierarchy and the concrete subclass represented by a particular row is identified by the value of a type discriminator column.

Example :



As in this example, we have 3 different java classes and all the properties are mapped to a single table employee. Apart from this, we have another column "emp_type" which acts as a discriminator between PermanentEmployee and ContractEmployee class.

Java Files :

Employee Class
package com.demo.example1.pojo;

public class Employee {

private String name;
private String email;
private Long empId;

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Long getEmpId() {
return empId;
}
public void setEmpId(Long empId) {
this.empId = empId;
}
}

PermanentEmployee  Class : 

package com.demo.example1.pojo;

import java.util.Date;

public class PermanentEmployee extends Employee {
private String designation;
private Date joiningDate;
public String getDesignation() {
return designation;
}
public void setDesignation(String designation) {
this.designation = designation;
}

public Date getJoiningDate() {
return joiningDate;
}
public void setJoiningDate(Date joiningDate) {
this.joiningDate = joiningDate;
}

}

ContractEmployee  Class : 
package com.demo.example1.pojo;

import java.util.Date;

public class ContractEmployee extends Employee {
private Date contractDate;

public Date getContractDate() {
return contractDate;
}

public void setContractDate(Date contractDate) {
this.contractDate = contractDate;
}

}

hbm files:
<hibernate-mapping package="com.demo.example1.pojo">
<class name="Employee" table="employee">

<id name="empId" column="empId" type="long">
<generator class="identity"/>
</id>

<discriminator column="emp_type" />

<property name="name" column="name" type="string"></property>
<property name="email" column="email"  type="string" ></property>


<subclass discriminator-value="pe" name="PermanentEmployee">
<property name="designation" column="designation" />
<property name="joiningDate" column="joiningDate" type="timestamp" />
</subclass>

<subclass discriminator-value="ce" name="ContractEmployee">
<property name="contractDate" column="contractDate" type="timestamp" />
</subclass>

</class>
</hibernate-mapping>


Main Class : 
package com.demo.example1;
import java.util.List;

import org.hibernate.Session;
import org.hibernate.Transaction;

import com.demo.example1.pojo.ContractEmployee;
import com.demo.example1.pojo.Employee;
import com.demo.example1.pojo.PermanentEmployee;


public class Test {
public static void main(String[] args) {
// First unit of work

Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();

PermanentEmployee pm = new PermanentEmployee();
pm.setDesignation("lead") ;
pm.setEmail("peter@test.com");
pm.setName("Peter") ;

session.save(pm);

ContractEmployee cm = new ContractEmployee();
cm.setEmail("simon@test.com");
cm.setName("Simon") ;
session.save(cm) ;

List<Employee> em = session.createQuery("from Employee").list();
System.out.println(em.size());

tx.commit();
session.close();

HibernateUtil.shutdown();
}

}

Output :

Hibernate:
    /* insert com.demo.example1.pojo.PermanentEmployee
        */ insert
        into
            employee_hie
            (name, email, designation, joiningDate, emp_type)
        values
            (?, ?, ?, ?, 'pe')
Hibernate:
    /* insert com.demo.example1.pojo.ContractEmployee
        */ insert
        into
            employee_hie
            (name, email, contractDate, emp_type)
        values
            (?, ?, ?, 'ce')
Hibernate:
    /*
from
    Employee */ select
        employee0_.empId as empId1_,
        employee0_.name as name1_,
        employee0_.email as email1_,
        employee0_.designation as designat5_1_,
        employee0_.joiningDate as joiningD6_1_,
        employee0_.contractDate as contract7_1_,
        employee0_.emp_type as emp2_1_
    from
        employee employee0_
2


Points to Note : 
  1. When defining properties in hbm file, make sure that discriminator is defined before we define any property element. 
  2. In case, if we dont have the freedom to add new discriminator column, we can apply a formula to calculate discriminator value for each row.
Example :
<discriminator formula="case when contractDate is not null then 'ce' else 'pe'  " />

Advantage: 
  1. This mapping is a winner in terms of both performance and simplicity. It's the best performing way to represent polymorphism.
  2. There is no complex join or subselects required which makes ad-hoc reporting possible.


Disadvantage :
  1. All properties declared by the subclass must be declared to be nullable. so loss of not null constraint may be serious problem from data integrity point of view.
  2. Voilates third normal form.


No comments:

Post a Comment