Wednesday, June 12, 2013

12 - Proxy Objects and Eager and Lazy Fetch Types



This tutorial is very important for hibernate, it provide Eager and Lazy Fetch Types.
Sometimes you have two entities and there's a relationship between them. For example, you might have an entity called UserDetails and another entity called Address.

The UserDetails entity might have some basic properties such as id, name, etc. as well as a property called listOfAddresses:

public class UserDetails {
 private int userId;                          
 private String name;
 private Collection<Address> listOfAddresses = new ArrayList<Address>();

 // setters and getters
}

Now when you load a UserDetails from the database, JPA loads its id and name fields for you. But you have two options for listOfAddresses: 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 UserDetails.getListOfAddresses() method.

When a UserDetails has many addresses it is not efficient to load all of its addresses with it when they are not needed. So in such like cases, you can declare that you want addresses to be loaded when they are actually needed. This is called lazy loading.

Lazy Initialization: Here you do not initialize entire object, you only initialize the first level variables (i.e. only first level of member variable of the object) then you only initialize the list only when you actually access it. FetchType LAZY - fetch when needed.
Eager Initialization: when loading an obj that has a collection, hibernate loads collection elements ONLY when actually you ask for them; so this improves performance, among other advantages. It should be used only when there is requirement else we need to strict to only Lazy initialization. FetchType EAGER fetch immediately.

*Hibernate provides proxy user class at back end to fetch the data from database.

HibernateTest.java
package org.yash.hibernate;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.yash.dto.Address;
import org.yash.dto.UserDetails;

public class HibernateTest {

      public static void main(String args[]){
            UserDetails user = new UserDetails();
            user.setUserName("First User");

            UserDetails user2 = new UserDetails();
            user2.setUserName("Second User");
           
            Address addr1 = new Address();
            addr1.setCity("Bentonville");
            addr1.setPincode("72712");
            addr1.setState("Ar");
            addr1.setStreet("2301 SE Saint Andrews");
           
            Address addr2 = new Address();
            addr2.setCity("Rogers");
            addr2.setPincode("72713");
            addr2.setState("AR");
            addr2.setStreet("2303 SE Saint Andrews");
           
            user.getListOfAddresses().add(addr1);
            user.getListOfAddresses().add(addr2);
           
            SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();         
            Session session = sessionFactory.openSession();
            /* It is used to save all the objects and to define single unit of work */
            session.beginTransaction();
           
            session.save(user);
            //session.save(user2);
            session.getTransaction().commit();
            session.close();
           
            user = null;
           
            session = sessionFactory.openSession();
            session.beginTransaction();
            user = (UserDetails) session.get(UserDetails.class, 1);
            System.out.println("User name retrieved is "+user.getUserName());
           
            //System.out.println(user.getListOfAddresses().size());
            session.close();
            /*
             * There is a possibility to use list of address after session close
             * We need to add @ElementCollection(fetch=FetchType.EAGER) in UserDetails calss
             * */      
            System.out.println("Size: "+user.getListOfAddresses().size());

      }
}

UserDetails.java
package org.yash.dto;

import java.util.ArrayList;
import java.util.Collection;
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.JoinTable;
import javax.persistence.Table;
import javax.persistence.JoinColumn;

@Entity
@Table(name="USER_DETAILS")
public class UserDetails {
      /* @Id says "userId" is primary key */
      @Id @GeneratedValue (strategy=GenerationType.AUTO)
      private int userId;    
      private String userName;
      @ElementCollection(fetch=FetchType.EAGER)
      @JoinTable(name="USER_ADDRESS",
                     joinColumns=@JoinColumn(name="USER_ID")
      )
      private Collection<Address> listOfAddresses = new ArrayList<Address>();
     
      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 void setListOfAddresses(Collection<Address> listOfAddresses) {
            this.listOfAddresses = listOfAddresses;
      }
      public Collection<Address> getListOfAddresses() {
            return listOfAddresses;
      }
}


Address.java
package org.yash.dto;

import javax.persistence.Column;
import javax.persistence.Embeddable;

@Embeddable
public class Address {
      @Column(name="STREET_NAME")
      private String street;
      @Column(name="CITY_NAME")
      private String city;
      @Column(name="STATE_NAME")
      private String state;
      @Column(name="PIN_CODE")
      private String pincode;
     
      public String getStreet() {
            return street;
      }
      public void setStreet(String street) {
            this.street = street;
      }
      public String getCity() {
            return city;
      }
      public void setCity(String city) {
            this.city = city;
      }
      public String getState() {
            return state;
      }
      public void setState(String state) {
            this.state = state;
      }
      public String getPincode() {
            return pincode;
      }
      public void setPincode(String pincode) {
            this.pincode = pincode;
      }
     
}

hibernate.cfg.xml
<?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.driver_class">
            org.postgresql.Driver
        </property>
        <property name="connection.url">
            jdbc:postgresql://localhost:5433/hibernatedb
        </property>
        <property name="connection.username">postgres</property>
        <property name="connection.password">admin</property>

        <!-- JDBC connection pool (use the built-in) -->
        <property name="connection.pool_size">1</property>

        <!-- SQL dialect -->
        <property name="dialect">
            org.hibernate.dialect.PostgreSQLDialect
        </property>

        <!-- Enable Hibernate's automatic session context management -->
        <property name="current_session_context_class">thread</property>
       
        <!-- Echo all executed SQL to stdout -->
        <property name="show_sql">true</property>
       
        <!-- Drop and re-create the database schema on startup -->
            <property name="hbm2ddl.auto">create</property> 
<!-- create / update -->
   
          <!-- Names the annotated entity class -->
          <mapping class="org.yash.dto.UserDetails"/>

    </session-factory>

</hibernate-configuration>



No comments:

Post a Comment