Java Hibernate 中子记录的延迟加载是如何工作的?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/22327715/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How Lazy loading of child records works in Hibernate?
提问by Sandy
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.
我知道上述问题很常见,但我只想知道 Hibernate 何时以及如何获取延迟加载的子记录。
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
雇员.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
员工.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
手机.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)
现在,这里的手机号码是 Employee 的子记录,它是根据 Employee.hbm.xml 中的 hibernate 配置由 Hibernate 延迟加载的(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.
在上面的代码中,在打印员工姓名后,我通过迭代 Set mobiles 来打印该员工的手机号码。
I have checked and found that the iteration of the mobileSet is actually fetching the mobileNumbers. i.e.
我查了一下,发现mobileSet的迭代实际上是在获取mobileNumbers。IE
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.
那么,它是如何发生的?因为我没有使用任何特定于休眠的 API 来获取延迟加载的子记录。只是迭代一组手机号码。
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?
那么 Hibernate 是如何在内部获取子记录的呢?当我遍历 mobileSet 时,Hibernate 正在做的后台工作是什么?
Please help in understanding my doubt.
请帮助理解我的疑问。
回答by Maxime
This topic could help you to understand what is the trick behind lazy loading :
本主题可以帮助您了解延迟加载背后的技巧:
回答by steelshark
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
在 Hibernate 中,只要访问延迟加载的属性并且它仍然由 em 管理,就会触发延迟加载。如果您在实体与实体管理器分离时访问延迟加载的属性,您将获得 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
将逻辑应用于您的示例: mobile.getMobileNumber() 将触发您的代码中的延迟加载。Hibernate 然后会自动向数据库发出查询。这是有效的,因为 hibernate 为延迟加载的属性实例化代理对象,一旦您尝试访问这些代理,hibernate 就会尝试从数据库加载它们。如果对象被管理,这将起作用,如果实体被分离,这将导致上述异常。根本不需要使用休眠 API 来触发延迟加载,它只会在访问延迟配置的属性时自动发生
回答by geoand
In your Employee.hbm.xml file you have stated that the fetch strategy for Mobile is lazy. That tells hibernate to generate a simple SQL statement to the table that corresponds to Employee without performing any joins with the corresponding Mobile table. However when employee.getMobiles() is called, Hibernate will issue a select query to the latter table using a where clause with the primary key of the former table.
在您的 Employee.hbm.xml 文件中,您已经声明 Mobile 的 fetch 策略是惰性的。这告诉 hibernate 为对应于 Employee 的表生成一个简单的 SQL 语句,而不与对应的 Mobile 表执行任何连接。然而,当employee.getMobiles() 被调用时,Hibernate 将使用带有前一个表的主键的where 子句向后一个表发出一个选择查询。
The way the fetching is implemented here leads to the infamous N+1 problem
这里实现获取的方式导致了臭名昭著的N+1 问题