Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How Lazy loading of child records works in Hibernate?

Tags:

java

hibernate

I know the above question is very common but I just wanted to know exactly when and How Hibernate is fetching the lazily loaded child records.

below is the sample table structure:

Table Structure:

employee_table(e_id, e_name, e_sal)
(100, XYZ, 20000)

mobile_table(m_id, m_number, e_id)
(1, 8728271817, 100)
(2, 0983813919, 100)

Employee.java

public class Employee implements Serializable {

    private static final long serialVersionUID = 1930751473454928876L;

    private long employeeId;
    private String employeeName;
    private double employeeSal;

    private Set<Mobile> mobiles;

    public long getEmployeeId() {
        return employeeId;
    }
    public void setEmployeeId(long employeeId) {
        this.employeeId = employeeId;
    }
    public String getEmployeeName() {
        return employeeName;
    }
    public void setEmployeeName(String employeeName) {
        this.employeeName = employeeName;
    }
    public double getEmployeeSal() {
        return employeeSal;
    }
    public void setEmployeeSal(double employeeSal) {
        this.employeeSal = employeeSal;
    }
    public Set<Mobile> getMobiles() {
        return mobiles;
    }
    public void setMobiles(Set<Mobile> mobiles) {
        this.mobiles = mobiles;
    }
}

Employee.hbm.xml

<hibernate-mapping>
    <class name="edu.sandip.hibernate.Employee" table="EMPLOYEE_T">
        <id name="employeeId" column="e_id" type="long">
            <generator class="increment" />
        </id>
        <property name="employeeName" column="e_name" type="string" />
        <property name="employeeSal" column="e_sal" type="double" />

        <set name="mobiles" table="MOBILE_T" inverse="true" lazy="true" fetch="select" >
            <key>
                <column name="e_id" not-null="true" />
            </key>
            <one-to-many class="edu.sandip.hibernate.Mobile" />
        </set>
    </class>
</hibernate-mapping>

Mobile.java

public class Mobile implements Serializable {

    private static final long serialVersionUID = 6279006639448045512L;

    private long mobId;
    private String mobileNumber;

    private Employee employee;

    public long getMobId() {
        return mobId;
    }
    public void setMobId(long mobId) {
        this.mobId = mobId;
    }
    public String getMobileNumber() {
        return mobileNumber;
    }
    public void setMobileNumber(String mobileNumber) {
        this.mobileNumber = mobileNumber;
    }
    public Employee getEmployee() {
        return employee;
    }
    public void setEmployee(Employee employee) {
        this.employee = employee;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((mobileNumber == null) ? 0 : mobileNumber.hashCode());
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Mobile other = (Mobile) obj;
        if (mobileNumber == null) {
            if (other.mobileNumber != null)
                return false;
        } else if (!mobileNumber.equals(other.mobileNumber))
            return false;
        return true;
    }
}

Mobile.hbm.xml

<hibernate-mapping>
    <class name="edu.sandip.hibernate.Mobile" table="MOBILE_T">
        <id name="mobId" column="m_id" type="long">
            <generator class="increment" />
        </id>
        <property name="mobileNumber" column="m_number" type="string" />
        <many-to-one name="employee" class="edu.sandip.hibernate.Employee" fetch="select">
            <column name="e_id" not-null="true" />
        </many-to-one>
    </class>
</hibernate-mapping>

Below is the code block to fetch the list of employees:

public List<Employee> getAllEmployees() {
    List<Employee> list = null;
    Session session = null;
    try {
        session = getSession();
        Query query = session.createQuery("FROM Employee");
        list = query.list();
        for(Employee employee : list) {
            System.out.println("Emaployee Name: " + employee.getEmployeeName);
            Set<Mobile> mobileSet = employee.getMobiles();
            System.out.println("Mobile Numbers: ");
            for(Mobile mobile : mobileSet) {
                System.out.println(mobile.getMobileNumber());
            }
        }
    } catch(Exception e) {
        e.printStackTrace();
    } finally {
        if(session != null) {
            System.out.println("session is still alive");
            releaseSession(session);
        }
    }
    return (list);
}

Now, Here the Mobile numbers is the child record of Employee which is been loaded by the Hibernate lazily as per the hibernate configuration in Employee.hbm.xml (lazy = true)

In the above code, after printing the employee name I am printing the mobile numbers of that employee by iterating the Set mobiles.

I have checked and found that the iteration of the mobileSet is actually fetching the mobileNumbers. i.e.

for(Mobile mobile : mobileSet) {
    System.out.println(mobile.getMobileNumber());
}

SO, How It is happening? Because I am not using any hibernate specific API to fetch the lazily loaded child records. Just Iterating over the set of mobile numbers.

Then how Hibernate is fetching the child records internally? What is the background job that Hibernate is doing while I am iterating over the mobileSet?

Please help in understanding my doubt.

like image 332
Sandy Avatar asked Oct 01 '22 08:10

Sandy


1 Answers

In hibernate lazy loading is triggered whenever a lazy loaded property is accessed and it is still managed by the em. If you would access a lazy loaded property when the entity is detached from the entity manager you would get a org.hibernate.LazyInitializationException

To apply the logic to your example: mobile.getMobileNumber() will trigger the lazy loading in your code. Hibernate will then automaticly issue a query to the database. This works because hibernate instantiates proxy objects for lazy loaded propertys, once you try to access these proxys hibernate tries to load them in from the database. this will work if the object is managed and this will result in forementioned exception if the entity is detached. There is no need at all to use a hibernate API to trigger the lazy loading it just happens automaticly when a lazy configured property is accessed

like image 167
steelshark Avatar answered Oct 05 '22 12:10

steelshark