Monday, August 20, 2012

Lazy/Eager loading using hibernate by example

Today's post will focus on why and how we use the concepts known as LAZY and EAGER loading in an application and how to use Spring's hibernate template to load our LAZY entities in an EAGER fashion.

And of course as the title itself suggests, we will show this by an example. The scenario is as such;

You are a parent who has a kid with a lot of toys. But the current issue is whenever you call him (we assume you have a boy), he comes to you with all his toys as well. Now this is an issue since you do not want him carrying around his toys all the time.

So being the rationale parent, you go right ahead and define the toys of the child as LAZY. Now whenever you call him, he just comes to you without his toys.

But you are faced with another issue. When the time comes for a family trip, you want him to bring along his toys because the kid will be bored with the trip otherwise. But since you strictly enforced LAZY on the child's toy, you are unable to ask him to bring along the toys. This is where EAGER fetching comes into play. Let us first see our domain classes.




 
package com.fetchsample.example.domain;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;

/**
 * Holds information about the child
 * 
 * @author dinuka.arseculeratne
 * 
 */
@Entity
@Table(name = "CHILD")
@NamedQuery(name = "findChildByName", query = "select DISTINCT(chd) from Child chd left join fetch chd.toyList where chd.childName=:chdName")
public class Child {

 public static interface Constants {
  public static final String FIND_CHILD_BY_NAME_QUERY = "findChildByName";

  public static final String CHILD_NAME_PARAM = "chdName";
 }

 @Id
 @GeneratedValue(strategy = GenerationType.AUTO)
 /**
  * The primary key of the CHILD table
  */
 private Long childId;

 @Column(name = "CHILD_NAME")
 /**
  * The name of the child
  */
 private String childName;

 @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
 /**
  * The toys the child has. We do not want the child to have the same toy more than
  * once, so we have used a set here.
  */
 private Set<Toy> toyList = new HashSet<Toy>();

 public Long getChildId() {
  return childId;
 }

 public void setChildId(Long childId) {
  this.childId = childId;
 }

 public String getChildName() {
  return childName;
 }

 public void setChildName(String childName) {
  this.childName = childName;
 }

 public Set<Toy> getToyList() {
  return toyList;
 }

 public void addToy(Toy toy) {
  toyList.add(toy);
 }

}

 
package com.fetchsample.example.domain;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 * Hols data related to the toys a child possess
 * 
 * @author dinuka.arseculeratne
 * 
 */
@Entity
@Table(name = "TOYS")
public class Toy {

 @Id
 @GeneratedValue(strategy = GenerationType.AUTO)
 @Column(name = "TOY_ID")
 /**
  * The primary key of the TOYS table
  */
 private Long toyId;

 @Column(name = "TOY_NAME")
 /**
  * The name of the toy
  */
 private String toyName;

 public Long getToyId() {
  return toyId;
 }

 public void setToyId(Long toyId) {
  this.toyId = toyId;
 }

 public String getToyName() {
  return toyName;
 }

 public void setToyName(String toyName) {
  this.toyName = toyName;
 }

 @Override
 public int hashCode() {
  final int prime = 31;
  int result = 1;
  result = prime * result + ((toyName == null) ? 0 : toyName.hashCode());
  return result;
 }

 @Override
 /**
  * Overriden because within the child class we use a Set to
  * hold all unique toys
  */
 public boolean equals(Object obj) {
  if (this == obj)
   return true;
  if (obj == null)
   return false;
  if (getClass() != obj.getClass())
   return false;
  Toy other = (Toy) obj;
  if (toyName == null) {
   if (other.toyName != null)
    return false;
  } else if (!toyName.equals(other.toyName))
   return false;
  return true;
 }

 @Override
 public String toString() {
  return "Toy [toyId=" + toyId + ", toyName=" + toyName + "]";
 }

}

So as you can see, we have two simple entities representing the child and toy. The child has a one-to-many relationship with the toys which means one child can have many toys (oh how i miss my childhood days). Afterwards we need to interact with the data, so let us go ahead and define out DAO(Data access object) interface and implementation.
 
package com.fetchsample.example.dao;

import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.fetchsample.example.domain.Child;

/**
 * The basic contract for dealing with the {@link Child} entity
 * 
 * @author dinuka.arseculeratne
 * 
 */
@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
public interface ChildDAO {

 /**
  * This method will create a new instance of a child in the child table
  * 
  * @param child
  *            the entity to be persisted
  */
 public void persistChild(Child child);

 /**
  * Retrieves a child without his/her toys
  * 
  * @param childId
  *            the primary key of the child table
  * @return the child with the ID passed in if found
  */
 public Child getChildByIdWithoutToys(Long childId);

 /**
  * Retrieves the child with his/her toys
  * 
  * @param childId
  *            the primary key of the child table
  * @return the child with the ID passed in if found
  */
 public Child getChildByIdWithToys(Long childId);

 /**
  * Retrieves the child by the name and with his/her toys
  * 
  * @param childName
  *            the name of the child
  * @return the child entity that matches the name passed in
  */
 public Child getChildByNameWithToys(String childName);

}


 
package com.fetchsample.example.dao.hibernate;

import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.fetchsample.example.dao.ChildDAO;
import com.fetchsample.example.domain.Child;

/**
 * The hibernate implementation of our {@link ChildDAO} interface
 * 
 * @author dinuka.arseculeratne
 * 
 */
public class ChildDAOHibernateImpl extends HibernateDaoSupport implements
  ChildDAO {

 /**
  * {@inheritDoc}
  */
 public void persistChild(Child child) {
  getHibernateTemplate().persist(child);
 }

 /**
  * {@inheritDoc}
  */
 public Child getChildByIdWithoutToys(Long childId) {
  return getHibernateTemplate().get(Child.class, childId);
 }

 /**
  * {@inheritDoc}
  */
 public Child getChildByIdWithToys(Long childId) {
  Child child = getChildByIdWithoutToys(childId);
  /**
   * Since by default the toys are not loaded, we call the hibernate
   * template's initialize method to populate the toys list of that
   * respective child.
   */
  getHibernateTemplate().initialize(child.getToyList());
  return child;
 }

 /**
  * {@inheritDoc}
  */
 public Child getChildByNameWithToys(String childName) {
  return (Child) getHibernateTemplate().findByNamedQueryAndNamedParam(
    Child.Constants.FIND_CHILD_BY_NAME_QUERY,
    Child.Constants.CHILD_NAME_PARAM, childName).get(0);

 }

}

A simple contract. I have four main methods. The first one of course just persists a child entity to the database.

The second method retrieves the Child by the primary key passed in, but does not fetch the toys.

The third method first fetches the Child and then retrieves the Child's toys using the Hibernate template's initialize() method. Note that when you call the initialize() method, hibernate will fetch you LAZY defined collection to the Child proxy you retrieved.

The final method also retrieves the Child's toys, but this time using a named query. If we go back to the Child entity's Named query, you can see that we have used "left join fetch". It is the keyword fetch that actually will initialize the toys collection as well when returning the Child entity that qualifies. Finally i have my main class to test our functionality;
 
package com.fetchsample.example;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.fetchsample.example.dao.ChildDAO;
import com.fetchsample.example.domain.Child;
import com.fetchsample.example.domain.Toy;

/**
 * A test class
 * 
 * @author dinuka.arseculeratne
 * 
 */
public class ChildTest {

 public static void main(String[] args) {
  ApplicationContext context = new ClassPathXmlApplicationContext(
    "com/fetchsample/example/spring-context.xml");

  /**
   * First we initialize a child
   */
  Child child = new Child();

  /**
   * A cool ben 10 action figure
   */
  Toy ben10 = new Toy();
  ben10.setToyName("Ben 10 figure");

  /**
   * A even more cooler spider man action figure
   */
  Toy spiderMan = new Toy();
  spiderMan.setToyName("Spider man figure");

  child.setChildName("John");
  /**
   * Add the toys to the collection
   */
  child.addToy(ben10);
  child.addToy(spiderMan);

  ChildDAO childDAO = (ChildDAO) context.getBean("childDAO");

  childDAO.persistChild(child);

  Child childWithoutToys = childDAO.getChildByIdWithoutToys(1L);
  // The following line will throw a lazy initialization error since we have
  // defined fetch type as LAZY in the Child domain class.
  // System.out.println(childWithToys.getToyList().size());

  Child childWithToys = childDAO.getChildByIdWithToys(1L);
  System.out.println(childWithToys.getToyList().size());

  Child childByNameWithToys = childDAO.getChildByNameWithToys("John");

  System.out.println(childByNameWithToys.getToyList().size());

 }
}

Defining your base Entity as LAZY is a good practice since in many occasions, you might not want the collections within an entity, but just want to interact with the data in your base entity. But in the event of you needing the data of your collections, then you can use either of the methods mentioned before.

That is about it for today. For anyone who wants to try out the example, i have uploaded it here.

 Thank you for reading, and hope you have a great day. Cheers!!

10 comments:

  1. Or just use JDO and use fetch groups, with a fraction of the code.

    ReplyDelete
  2. I have not used JDO. But will give it a try and see. Thx for leaving by your feedback.

    Cheers

    ReplyDelete
  3. Extremely well presented. It will be useful to see the same case presented without the Spring framework.

    ReplyDelete
  4. Thank you very much for your kind feedback. I will take your request into consideration.

    Cheers

    Dinuka

    ReplyDelete
  5. Hi,
    Nice One!!! I liked the way you took the practical and simple analogy to explain your concept. Good work.

    ReplyDelete
  6. Hi Sree,

    Glad you liked it. Thank you for leaving by a comment.

    Cheers

    ReplyDelete
  7. nice one it will be helpful for us..
    if you tell more about jdo it will be good to understand.
    thanks

    ReplyDelete
  8. Hi,
    This is good One!!! I totally understand the way you explained thanks for sharing. Good work.

    ReplyDelete
  9. Hi Krishnan,

    Thank you for leaving by this kind comment.

    ReplyDelete