Proxy Objects and Eager & Lazy Fetch Types in Hibernate

In this tutorial we look what is proxy object and how hibernate provide the proxy object for us and also we look about some fetching strategies.

So we first look about the proxy object.
Hibernate Proxy Object:  An object proxy is just a way to avoid retrieving an object until you need it.

In our example user class has the three field values-
1. User Id
2. User Name
3. List of the Addresses

If you want retrieve user object from the database, so what field value are retrieved from the database and which field are initialized. Suppose one user XYZ has the 100 addresses in the database and we want to retrieved the name of this user from database. For that we retrieve the user object, now question is this what about the field listOfAddress field, Is it also have the value? if you say yes so what about cost of memory ? if you say no so how to retrieve the value associated with that field the address table in the database on demand.
-A
 |
 *---B
 |   |
 |   *---C
 |   |
 |   *---D
 |       |
 |       *---E
 |       |
 |       *---F
 |       |
 |       *---G
 *---H
 Ok lets see in the given below flow of the diagram.
  
Proxy Objects and Eager & Lazy Fetch Types in Hibernate

In  Hibernate 2 does not proxy objects by default. However, experience has shown that using object proxies is preferred, so this is the default in Hibernate 3.

In Hibernate 3, when we fetching the user object from the database actually its retrieved the proxy object of the user class means only first level of the fields are initializing with the associated values from the database. Field listOfAddresses does not have the value. If you want the list address you should call the following method you will get the listOfAddresses.
                                        user.getListOfAddress(); --->> this return the list of the address associated with that particular user which name is XYZ this is the default behavior of the Hibernate 3

Now we look about the fetching strategies.
Fetching Strategies:  there are two types of the fetching strategies in the hibernate.
1. Lazy Fetch type
2. Eager Fetch type
                  LAZY = fetch when needed
                  EAGER = fetch immediately
1. Lazy Fetch Type: This the default fetch type of the hibernate 3.
Now when you load a User from the database, JPA loads its id, name, and address fields for you. But you have two options for users: to load it together with the rest of the fields (i.e. eagerly) or to load it on-demand (i.e. lazily) when you call the user's getListOfAddresses() method.

Lazy/Select Fetch strategy:- Select Fetch strategy is the lazy fetching of associations. The purpose of Lazy strategy is memory optimization . When I say memory optimization it means it means it saves us from heap error. This is what I think. So we can say yes if we are loading too objects in aseesion we should go for Lazy Fetch strategy but in terms of time performance it does not provide any Benefit. Agreed?

When a user has many addresses it is not efficient to load all of its addresses with it when they are not needed. So in suchlike cases, you can declare that you want addresses to be loaded when they are actually needed. This is called lazy loading.
EXAMPLE: UserDetails.java
package com.sdnext.hibernate.tutorial.dto;

import java.util.ArrayList;
import java.util.Collection;

import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.Table;

@Entity
@Table (name="USER_DETAIL")

public class UserDetails
{
    @Id
    @Column(name="USER_ID")
    @GeneratedValue(strategy=GenerationType.AUTO)

    private int    userId;
   
    @Column(name="USER_NAME")
    private String userName;
   
    @ElementCollection(fetch=FetchType.LAZY)
    @JoinTable(name="USER_ADDRESS",    joinColumns=@JoinColumn(name="USER_ID"))

    private Collection<Address> lisOfAddresses = new ArrayList<Address>();
   
    public Collection<Address> getLisOfAddresses() {
        return lisOfAddresses;
    }
    public void setLisOfAddresses(Collection<Address> lisOfAddresses) {
        this.lisOfAddresses = lisOfAddresses;
    }
    public int getUserId() {
        return userId;
    }
    public void setUserId(int userId) {
        this.userId = userId;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public String toString()
    {
        return "[User Name: "+userName+"\n Office Address: "+lisOfAddresses+"]";
    }
}

Now hibernate.cfg.xml and Address.java are same as the previous tutorial.

Now run the this with following class.

HibernateTestDemo.java
package com.sdnext.hibernate.tutorial;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;

import com.sdnext.hibernate.tutorial.dto.Address;
import com.sdnext.hibernate.tutorial.dto.UserDetails;

public class HibernateTestDemo {
    /**
     * @param args
     */
    public static void main(String[] args)
    {
        UserDetails user = new UserDetails();  // create user object
        //user.setUserId(1);
        user.setUserName("Dinesh Rajput");
       
        Address address1 = new Address(); // create address object
        address1.setStreet("First Street");
        address1.setCity("First City");
        address1.setState("First State");
        address1.setPincode("First Pin");
       
        Address address2 = new Address(); // create another address object
        address2.setStreet("Second Street");
        address2.setCity("Second City");
        address2.setState("Second State");
        address2.setPincode("Second Pin");
       
        user.getLisOfAddresses().add(address1); // set the addresses objects to list of the addresses
        user.getLisOfAddresses().add(address2);
       
        SessionFactory sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory(); // create session factory object
        Session session = sessionFactory.openSession(); // create session object
        session.beginTransaction(); // start transaction object
        session.save(user); // save the user to database
        session.getTransaction().commit(); // commit the transaction
        session.close(); // closing session
       
         session = sessionFactory.openSession(); // again create another session object 
         user = null;
         user = (UserDetails) session.get(UserDetails.class, 1); // retrieved the user from the database for particular user which user id = 2 this object it is proxy user object.
         System.out.println(user.getLisOfAddresses().size());
    }
}
******************************************************************************
OUTPUT:Look care fully the output has the two select query one is for user and another is for address table when we call getListOfAddresses();


log4j:WARN No appenders could be found for logger (org.hibernate.cfg.annotations.Version).
log4j:WARN Please initialize the log4j system properly.
Hibernate: insert into USER_DETAIL (USER_NAME) values (?)
Hibernate: insert into USER_ADDRESS (USER_ID, CITY_NAME, PIN_CODE, STATE_NAME, STREET_NAME) values (?, ?, ?, ?, ?)
Hibernate: insert into USER_ADDRESS (USER_ID, CITY_NAME, PIN_CODE, STATE_NAME, STREET_NAME) values (?, ?, ?, ?, ?)
Hibernate: select userdetail0_.USER_ID as USER1_0_0_, userdetail0_.USER_NAME as USER2_0_0_ from USER_DETAIL userdetail0_ where userdetail0_.USER_ID=?
Hibernate: select lisofaddre0_.USER_ID as USER1_0_, lisofaddre0_.CITY_NAME as CITY2_0_, lisofaddre0_.PIN_CODE as PIN3_0_, lisofaddre0_.STATE_NAME as STATE4_0_, lisofaddre0_.STREET_NAME as STREET5_0_ from USER_ADDRESS lisofaddre0_ where lisofaddre0_.USER_ID=?
2
******************************************************************************
Now if you change to some lines of code for this class file to verify the PROXY object.
Before calling getListOfAddresses() method close the session then look what happens.


         session = sessionFactory.openSession(); // again create another session object 
         user = null;
         user = (UserDetails) session.get(UserDetails.class, 1); // retrieved the user from the database for particular user which user id = 2 this object it is proxy user object.
          session.close(); // close the session before calling collection getter
         System.out.println(user.getLisOfAddresses().size());

Now Output: 
log4j:WARN No appenders could be found for logger (org.hibernate.cfg.annotations.Version).
log4j:WARN Please initialize the log4j system properly.
Hibernate: insert into USER_DETAIL (USER_NAME) values (?)
Hibernate: insert into USER_ADDRESS (USER_ID, CITY_NAME, PIN_CODE, STATE_NAME, STREET_NAME) values (?, ?, ?, ?, ?)
Hibernate: insert into USER_ADDRESS (USER_ID, CITY_NAME, PIN_CODE, STATE_NAME, STREET_NAME) values (?, ?, ?, ?, ?)
Hibernate: select userdetail0_.USER_ID as USER1_0_0_, userdetail0_.USER_NAME as USER2_0_0_ from USER_DETAIL userdetail0_ where userdetail0_.USER_ID=?
Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.sdnext.hibernate.tutorial.dto.UserDetails.lisOfAddresses, no session or session was closed
    at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException
(AbstractPersistentCollection.java:380)
    at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected
(AbstractPersistentCollection.java:372)
    at org.hibernate.collection.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:119)
    at org.hibernate.collection.PersistentBag.size(PersistentBag.java:248)
    at com.sdnext.hibernate.tutorial.HibernateTestDemo.main(HibernateTestDemo.java:48)
******************************************************************************
As above code failed run because when we are using the LAZY Type strategy of fetching an object hibernate session return the proxy object, it exist in the session if we are closing the session the lazy loading is not happening. 
Proxy Objects in Hibernate


Lazy Fetch Types in Hibernate

2. Eager Fetch Strategy: In hibernate 2 this is default behavior of the to retrieving an object from the database.
Eager/Join Fetch strategy:- Join Fetch strategy the eager fetching of associations.The purpose of Join Fetch strategy is optimization in terms of time.I mean even associations are fetched right at the time of fetching parent object. So in this case we don’t make database call again and again . So this will be much faster.Agreed that this will bad if we are fetching too many objects in a session because we can get java heap error.

Eager Fetch Types in Hibernate

Now we look on the code for EAGER LOADING.

UserDetais.java
package com.sdnext.hibernate.tutorial.dto;

import java.util.ArrayList;
import java.util.Collection;

import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.Table;

@Entity
@Table (name="USER_DETAIL")
public class UserDetails
{
    @Id
    @Column(name="USER_ID")
    @GeneratedValue(strategy=GenerationType.AUTO)
    private int    userId;
   
    @Column(name="USER_NAME")
    private String userName;
   
    @ElementCollection(fetch=FetchType.EAGER)
    @JoinTable(name="USER_ADDRESS",    joinColumns=@JoinColumn(name="USER_ID"))
    private Collection<Address> lisOfAddresses = new ArrayList<Address>();
   
    public Collection<Address> getLisOfAddresses() {
        return lisOfAddresses;
    }
    public void setLisOfAddresses(Collection<Address> lisOfAddresses) {
        this.lisOfAddresses = lisOfAddresses;
    }
    public int getUserId() {
        return userId;
    }
    public void setUserId(int userId) {
        this.userId = userId;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public String toString()
    {
        return "[User Name: "+userName+"\n Office Address: "+lisOfAddresses+"]";
    }
}

Now run the following code and see the output---
package com.sdnext.hibernate.tutorial;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.AnnotationConfiguration;

import com.sdnext.hibernate.tutorial.dto.Address;
import com.sdnext.hibernate.tutorial.dto.UserDetails;

public class HibernateTestDemo {
    /**
     * @param args
     */
    public static void main(String[] args)
    {
        UserDetails user = new UserDetails();
        //user.setUserId(1);
        user.setUserName("Dinesh Rajput");
       
        Address address1 = new Address();
        address1.setStreet("First Street");
        address1.setCity("First City");
        address1.setState("First State");
        address1.setPincode("First Pin");
       
        Address address2 = new Address();
        address2.setStreet("Second Street");
        address2.setCity("Second City");
        address2.setState("Second State");
        address2.setPincode("Second Pin");
       
        user.getLisOfAddresses().add(address1);
        user.getLisOfAddresses().add(address2);
       
        SessionFactory sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
        Session session = sessionFactory.openSession();
        session.beginTransaction();
        session.save(user);
        session.getTransaction().commit();
        session.close();
       
         session = sessionFactory.openSession();
         user = null;
         user = (UserDetails) session.get(UserDetails.class, 1);
         session.close(); //closing the session before calling collection getter
         System.out.println(user.getLisOfAddresses().size());
      }
}


OUTPUT:
log4j:WARN No appenders could be found for logger (org.hibernate.cfg.annotations.Version).
log4j:WARN Please initialize the log4j system properly.
Hibernate: insert into USER_DETAIL (USER_NAME) values (?)
Hibernate: insert into USER_ADDRESS (USER_ID, CITY_NAME, PIN_CODE, STATE_NAME, STREET_NAME) values (?, ?, ?, ?, ?)
Hibernate: insert into USER_ADDRESS (USER_ID, CITY_NAME, PIN_CODE, STATE_NAME, STREET_NAME) values (?, ?, ?, ?, ?)
Hibernate: select userdetail0_.USER_ID as USER1_0_0_, userdetail0_.USER_NAME as USER2_0_0_, lisofaddre1_.USER_ID as USER1_2_, lisofaddre1_.CITY_NAME as CITY2_2_, lisofaddre1_.PIN_CODE as PIN3_2_, lisofaddre1_.STATE_NAME as STATE4_2_, lisofaddre1_.STREET_NAME as STREET5_2_ from USER_DETAIL userdetail0_ left outer join USER_ADDRESS lisofaddre1_ on userdetail0_.USER_ID=lisofaddre1_.USER_ID where userdetail0_.USER_ID=?
2******************************************************************************
See this is successfully run the code because we are using the EAGER fetching strategy so in this strategy session return the original object with all field are initialized when load on the memory.
 In the above output string look care fully there are only one select statement with join clause.

So now can we say in the hibernate session where we are not loading too many objects we should go for Eager fetch as it will be much better in terms of time response(Any ways memory will be reclaimed by garbage collector once we close the session).

eager in hibernate


In Next Chapter we will discuss about One to One Mapping.

                    <<Previous Chapter 15<<    >>Next Chapter17>>