Thursday, August 11, 2011

HIBERNATE - Bi-directional One-to-Many Association

In the following example, we will be going through the bi-directional One-to-Many association using Hibernate. We will be using the Employee and PhoneNumbers table where each Employee will have one or more phone numbers associated with him/her.

Schema Creation:

We will be creating two tables Employee and PhoneNumbers. The PhoneNumbers table has a foreign key reference to the Employee table.

CREATE TABLE EMPLOYEE (
	EMP_ID INTEGER(5) PRIMARY KEY DEFAULT 1,
	EMP_NAME VARCHAR(30) NOT NULL,
	EMP_DOB DATE NOT NULL
);

CREATE TABLE PHONENUMBERS (
	PH_ID INTEGER(5) PRIMARY KEY DEFAULT 1,
	PH_NUMBER VARCHAR(10) NOT NULL,
	EMP_ID INTEGER(5) NOT NULL,
	FOREIGN KEY (EMP_ID) REFERENCES EMPLOYEE (EMP_ID)
);

HBM Files Creation:

Employee.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">
<hibernate-mapping package="org.mybusiness.pojos">
    <class name="Employee" table="EMPLOYEE">
        <id name="empId" column="EMP_ID" type="integer">
            <generator class="increment"></generator>
        </id>

        <property name="empName" column="EMP_NAME" not-null="true"
            type="string"></property>

        <property name="empDOB" column="EMP_DOB" not-null="true"
            type="date"></property>

        <set name="phoneNumbers" table="PHONENUMBERS" cascade="all"
            lazy="false" inverse="true" order-by="PH_ID ASC">
            <key column="EMP_ID" not-null="true"></key>
            <one-to-many class="PhoneNumbers" />
        </set>
    </class>
</hibernate-mapping>

PhoneNumbers.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">
<hibernate-mapping package="org.mybusiness.pojos">
    <class name="PhoneNumbers" table="PHONENUMBERS">
        <id name="phId" column="PH_ID" type="integer">
            <generator class="increment"></generator>
        </id>

        <property name="phoneNo" column="PH_NUMBER" not-null="true"
            type="string"></property>

        <many-to-one name="employee" column="EMP_ID"
            not-null="true" cascade="all" class="Employee" lazy="false"></many-to-one>
    </class>
</hibernate-mapping>

Java Files:

Employee.java:

package org.mybusiness.pojos;

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

public class Employee implements Serializable {

    private static final long serialVersionUID = 4451804446997564426L;

    private int empId;
    private String empName;
    private Date empDOB;

    // Hibernate expects interfaces like Set, Map, List to be the declaration
    // because while doing retrieve operations Hibernate uses its own Set, etc
    // to fill up the contents in this variable
    private Set<PhoneNumbers> phoneNumbers;

    public Employee() {
        phoneNumbers = new LinkedHashSet<PhoneNumbers>();
    }

    public int getEmpId() {
        return empId;
    }

    public void setEmpId(int empId) {
        this.empId = empId;
    }

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    public Date getEmpDOB() {
        return empDOB;
    }

    public void setEmpDOB(Date empDOB) {
        this.empDOB = empDOB;
    }

    public Set<PhoneNumbers> getPhoneNumbers() {
        return phoneNumbers;
    }

    public void setPhoneNumbers(Set<PhoneNumbers> phoneNumbers) {
        this.phoneNumbers = phoneNumbers;
    }

    public void addPhoneNumber(PhoneNumbers phoneNumber) {
        this.phoneNumbers.add(phoneNumber);
    }
}

PhoneNumbers.java:

package org.mybusiness.pojos;

import java.io.Serializable;

public class PhoneNumbers implements Serializable {

    private static final long serialVersionUID = -3505306499633395745L;

    private int phId;
    private String phoneNo;

    // Adding Bi-directionality.
    private Employee employee;

    public int getPhId() {
        return phId;
    }

    public void setPhId(int phId) {
        this.phId = phId;
    }

    public String getPhoneNo() {
        return phoneNo;
    }

    public void setPhoneNo(String phoneNo) {
        this.phoneNo = phoneNo;
    }

    public Employee getEmployee() {
        return employee;
    }

    public void setEmployee(Employee employee) {
        this.employee = employee;
    }
}

Functionalities:

Create Employee:

Approach 1:

public void createEmployeeBidirApproach1() {

    HibernateTemplate ht = new HibernateTemplate(sessionFactory);

    Session s = ht.getSessionFactory().openSession();
    Transaction tx = s.beginTransaction();

    try {

        // Create an employee
        Employee emp = new Employee();
        emp.setEmpName("Peter");
        emp.setEmpDOB(Calendar.getInstance().getTime());

        // Save the Employee and get the Primary Key
        Integer key = (Integer) s.save(emp);

        PhoneNumbers phNo = new PhoneNumbers();
        phNo.setPhoneNo("8765432190");
        phNo.setEmployee(emp); // Set the foreign key.

        // Save the phone number
        s.save(phNo);

        phNo = new PhoneNumbers();
        phNo.setPhoneNo("9988776655");
        phNo.setEmployee(emp);// Save the phone number

        // Save the phone number
        s.save(phNo);

        tx.commit();
    } catch (Exception e) {
        e.printStackTrace();
        tx.rollback();
    } finally {
        s.close();
    }
}

Approach 2:

public void createEmployeeBidirApproach2() {

    HibernateTemplate ht = new HibernateTemplate(sessionFactory);

    Session s = ht.getSessionFactory().openSession();
    Transaction tx = s.beginTransaction();

    try {

        Employee emp = new Employee();
        emp.setEmpName("Jake");
        emp.setEmpDOB(Calendar.getInstance().getTime());

        // Save Employee and get the key
        Integer key = (Integer) s.save(emp);

        PhoneNumbers phNo = new PhoneNumbers();
        phNo.setPhoneNo("7865765412");
        phNo.setEmployee(emp);

        emp.addPhoneNumber(phNo);

        phNo = new PhoneNumbers();
        phNo.setPhoneNo("7766776677");
        phNo.setEmployee(emp);

        emp.addPhoneNumber(phNo);

        // Save the employee again with the phone numbers added
        s.save(emp);
        tx.commit();
    } catch (Exception e) {
        e.printStackTrace();
        tx.rollback();
    } finally {
        s.close();
    }
}

Retrieve Employee:

public void getAllEmployees() {

    HibernateTemplate ht = new HibernateTemplate(sessionFactory);

    DetachedCriteria criteria = DetachedCriteria.forClass(Employee.class,
            "emp");
    criteria.add(Restrictions.le("emp.empId", 10));

    // NOTE: LEFT_JOIN eliminates the selection of all phone numbers from
    // the phonenumbers table. This is necessary when we want to filter out
    // the result set from the dependent entity
    DetachedCriteria child = criteria.createCriteria("emp.phoneNumbers",
            "ph", CriteriaSpecification.LEFT_JOIN);
    child.add(Restrictions.ilike("ph.phoneNo", "2", MatchMode.END));
    child.add(Restrictions.le("ph.phId", 10));
    child.addOrder(Order.desc("ph.phId"));

    // NOTE: The result transformer ensures that the result contains only
    // one Employee object with set of phone numbers. If this transformer is
    // not used, then the result set would be repeated by the number of
    // phone numbers present in the set.
    criteria
            .setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);

    List<Employee> employees = ht.findByCriteria(criteria);

    int count = 1;
    for (Employee employee : employees) {
        System.out.println("Employee Index : " + count++);
        System.out.println("----------------------------");
        printEmployee(employee);
    }
}

public void getPhoneNumbersForEmployee() {

    Integer empId = 5;

    HibernateTemplate ht = new HibernateTemplate(sessionFactory);

    DetachedCriteria criteria = DetachedCriteria.forClass(
            PhoneNumbers.class, "ph");
    criteria.createAlias("ph.employee", "emp");

    criteria.add(Restrictions.eq("emp.empId", 5));

    List<PhoneNumbers> phonenumbers = ht.findByCriteria(criteria);

    for (PhoneNumbers phNo : phonenumbers) {
        System.out.println(phNo.getPhId() + ":" + phNo.getPhoneNo() + ":"
                + phNo.getEmployee().getEmpName());
        System.out.println();
    }
}

private void printEmployee(Employee employee) {

    System.out.println("Employee Id : " + employee.getEmpId());
    System.out.println("Employee Name : " + employee.getEmpName());
    System.out.println("Employee DOB : " + employee.getEmpDOB());

    Set<PhoneNumbers> phNos = employee.getPhoneNumbers();
    System.out.println(phNos.getClass());

    if (phNos != null) {
        for (PhoneNumbers phNo : phNos) {
            System.out.println("PhNo : " + phNo.getPhId() + ", "
                    + phNo.getPhoneNo());
        }
    }
    System.out.println("---------------------------"
            + "--------------------------");
}

No comments:

Post a Comment