Saturday, August 27, 2011

Hibernate Inheritance with Annotations

 There are a few approaches to deal with inheritance in hibernate. The technique discussed in this post accommodates all in one table.
  I'm having an abstract class, 'Person' and two of it's subclasses, 'Student' and 'Employee'. Both 'Student' and 'Employee' information is to be stored in a single table, named 'person'.

==================================================================
package com.shyarmal.hibernate;

import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;

@Entity
@Table(name = "person")
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "person_type", discriminatorType = DiscriminatorType.STRING)
public abstract class Person {

    private Long id;
    protected String nic;
    protected int age;
    protected String name;
    protected char sex;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    public Long getId() {
        return id;
    }
   
    public void setId(Long id) {
        this.id = id;
    }
   
    @Column(name = "nic", unique = true, nullable =false)
    public String getNic() {
        return nic;
    }

    public void setNic(String nic) {
        this.nic = nic;
    }

    @Column(name = "age")
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Column(name = "name", nullable = false, length = 30)
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Column(name = "gender", length = 2, nullable = false)
    public char getSex() {
        return sex;
    }

    public void setSex(char sex) {
        this.sex = sex;
    }
}

===================================================================

  First, we'll have a look at the 'Person' class. Here I'll only discuss what's done to accomplish inheritance [For other basic information, refer to Hinernate Annotations Example].  The annotation, @Inheritance denotes inheritance and there are subclasses of person to be mapped. Moreover all 'Person' instances will be available in a single database table, since the inheritance strategy used is  'InheritanceType.SINGLE_TABLE'.   
   @DiscriminatorColumn is mandatory in this approach. It forms a new column in the database with the name specified in the 'name' attribute. The discriminator column is used to Identify the type of the record (in our example where a record is of type 'Student' or 'Employee'). The attribute 'discriminatorType' states the type of the discriminator column.
   I have posted my 'Student' and 'Employee' classes below.

=================================================================== 
package com.shyarmal.hibernate;

import java.util.List;

import javax.persistence.Column;
import javax.persistence.DiscriminatorValue;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;

@Entity
@DiscriminatorValue("student")
public class Student extends Person {

    private String college;
    private List<String> subjects;

    @Column(name = "college", nullable = false, columnDefinition = "varchar(20) default 'unknown'")
    public String getCollege() {
        return college;
    }

    public void setCollege(String college) {
        this.college = college;
    }
   
    @ElementCollection(fetch = FetchType.LAZY)
    @JoinTable(name = "student_subjects", joinColumns = {@JoinColumn(name = "person_id")})
    public List<String> getSubjects() {
        return subjects;
    }

    public void setSubjects(List<String> subjects) {
        this.subjects = subjects;
    }

    @Override
    public String toString() {
        return String.format("student: nic [%s], college [%s], name [%s], age [%d]", nic, college, name, age);
    }
}

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

package com.shyarmal.hibernate;

import javax.persistence.Column;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;

@Entity
@DiscriminatorValue("employee")
public class Employee extends Person {

    private String company;
    private double salary;

    @Column(name = "company", nullable = false, columnDefinition = "varchar(20) default 'unknown'")
    public String getCompany() {
        return company;
    }

    public void setCompany(String company) {
        this.company = company;
    }

    @Column(name = "salary", nullable = false, columnDefinition = "double(8, 2) default '0.0'")
    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return String.format("employee: nic [%s], company [%s], name [%s], age [%d]", nic, company, name, age);
    }
}
===================================================================

@DiscriminatorValue annotation should be present in the subclasses. The discriminator value is what's saved in the discriminator column, which was discussed above [in our example if an employee is saved the discriminator column will have the value 'employee' and for a student the value will be 'student']. Notice 'columnDefinition' attribute in @Column in subclasses. If a column is declared not to be nullable, then a default value for the corresponding field should be given as done in the example. If not hibernate does not create the table. 'Student' class has a list of subjects. The methodology used is described here.

Below is the hibernate.cfg.xml used.
=================================================================== 

<?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="connection.url">jdbc:mysql://localhost/test</property>
        <property name="connection.username">root</property>
        <property name="connection.password">123</property>
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="show_sql">true</property>
        <property name="format_sql">true</property>
        <property name="hbm2ddl.auto">create</property>
        <property name="connection.pool_size">1</property>
        <property name="current_session_context_class">thread</property>
        <mapping class="com.shyarmal.hibernate.Person"/>
        <mapping class="com.shyarmal.hibernate.Student"/>
        <mapping class="com.shyarmal.hibernate.Employee"/> 
    </session-factory>
</hibernate-configuration>


thanks,
Shyarmal.

No comments:

Post a Comment