Powered By Blogger
Showing posts with label inheritance. Show all posts
Showing posts with label inheritance. Show all posts

Sunday, August 12, 2012

Hibernate Hierarchy Mapping - Table Per Class

This example shows how to map a hierarchy with hibernate using the strategy, table per class. Circle and Square are two classes which extend Shape. The attribute, id of Shape is common to both the subclasses and will be the primary key of the tables created.

===================================================================
// The shape class
package com.shyarmal.hibernate.example.shape;

public class Shape implements IPersistable {

    protected long id;

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }
}


// Subclass of Shape, Circle
package com.shyarmal.hibernate.example.shape;

public class Circle extends Shape {

    private float radius;
    private float centerX;
    private float centerY;

    public float getRadius() {
        return radius;
    }

    public void setRadius(float radius) {
        this.radius = radius;
    }

    public float getCenterX() {
        return centerX;
    }

    public void setCenterX(float centerX) {
        this.centerX = centerX;
    }

    public float getCenterY() {
        return centerY;
    }

    public void setCenterY(float centerY) {
        this.centerY = centerY;
    }
}


// Subclass of Shape, Square
package com.shyarmal.hibernate.example.shape;

public class Square extends Shape {

    private float length;
    private float topX;
    private float topY;

    public float getTopX() {
        return topX;
    }

    public void setTopX(float topX) {
        this.topX = topX;
    }

    public float getlength() {
        return length;
    }

    public void setlength(float length) {
        this.length = length;
    }

    public float getTopY() {
        return topY;
    }
    public void setTopY(float topY) {
        this.topY = topY;
    }
}

===================================================================
The hibernate xml mapping for the above hierarchy is as follows. All three classes mapping configurations are done in the same hmb file. This configuration will map (and/or create) three tables for the three classes.

===================================================================
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.shyarmal.hibernate.example.shape">
    <class name="Shape" table="SHAPE">
        <id name="id" column="ID" type="long">
            <generator class="native" />
        </id>
        <joined-subclass table="CIRCLE" name="Circle">
            <key column="ID" />
            <property name="radius" column="RADIUS" type="float"/>
            <property name="centerX" column="CENTER_X" type="float"/>
            <property name="centerY" column="CENTER_Y" type="float"/>
        </joined-subclass>
        <joined-subclass table="SQUARE" name="Square">
            <key column="ID" />       
            <property name="length" type="float" column="LENGTH"/>
            <property name="topY" column="TOP_Y" type="float" />
            <property name="topX" column="TOP_X" type="float" />
        </joined-subclass>
    </class>
</hibernate-mapping>

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

As you can notice, the 'class' tag refers to the class, Shape and the primary key is it's id attribute  (mapped with 'generator' tag). Subclasses are mapped with the element, 'joined-subclass', enclosed in 'class' tag. The element, 'key' defines a (foreign key) reference to the 'ID' column of the 'SHAPE' table. Further, the properties specific to each subclass is defined within the 'joined-subclass' element.

thanks,
Shyarmal.

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.