如何在Hibernate中加载子记录的延迟?

时间:2014-03-11 13:50:46

标签: java hibernate

我知道上面的问题很常见,但我只是想知道Hibernate何时以及如何获取延迟加载的子记录。

下面是示例表结构:

表格结构:

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>

以下是获取员工列表的代码块:

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);
}

现在,这里的移动号是Employee的子记录,它根据Employee.hbm.xml中的hibernate配置懒惰地加载了Hibernate(lazy = true)

在上面的代码中,打印员工姓名后,我打印的是手机号码 员工通过迭代Set手机。

我已经检查过并发现mobileSet的迭代实际上正在获取mobileNumbers。 即。

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

那么,它是如何发生的?因为我没有使用任何特定于hibernate的API来获取延迟加载的子记录。只是迭代一组手机号码。

然后Hibernate如何在内部获取子记录?在迭代mobileSet时Hibernate正在做什么后台工作?

请帮助理解我的疑问。

3 个答案:

答案 0 :(得分:2)

在hibernate中,只要访问延迟加载的属性并且它仍由em管理,就会触发延迟加载。如果在实体与实体管理器分离时访问延迟加载的属性,则会得到org.hibernate.LazyInitializationException

要将逻辑应用于您的示例: mobile.getMobileNumber()将触发代码中的延迟加载。然后,Hibernate将自动向数据库发出查询。这是因为hibernate为延迟加载的属性实例化代理对象,一旦你尝试访问这些代理,hibernate会尝试从数据库加载它们。如果对象被管理,这将起作用,如果实体被分离,这将导致前面提到的异常。根本不需要使用hibernate API来触发延迟加载,它只是在访问惰性配置属性时自动发生

答案 1 :(得分:1)

在您的Employee.hbm.xml文件中,您已声明Mobile的抓取策略是惰性的。 这告诉hibernate为与Employee对应的表生成一个简单的SQL语句,而不与相应的Mobile表进行任何连接。 但是当调用employee.getMobiles()时 Hibernate将使用带有前表的主键的where子句向后一个表发出select查询。

这里实现提取的方式会导致臭名昭着的N+1 problem

答案 2 :(得分:0)

本主题可以帮助您了解延迟加载背后的技巧:

How proxy loads the lazy property in Hibernate/JPA