Hibernate 中主要获取类型
1. 概述
使用 ORM 时,数据获取/加载可以分为两种类型:eager 和 lazy。
在这个快速教程中,我们将指出差异并展示我们如何在 Hibernate 中使用它们。
2. Maven依赖
为了使用 Hibernate,让我们首先在pom.xml中定义主要依赖项:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.2.2.Final</version>
</dependency>
可以在此处 找到最新版本的 Hibernate 。
3. Eager和Lazy加载
我们应该在这里讨论的第一件事是什么是lazy加载和eager加载:
- Eager加载是一种在现场进行数据初始化的设计模式。
- Lazy加载是一种设计模式,我们使用它来尽可能推迟对象的初始化。
让我们看看这是如何工作的。
首先,我们来看一下UserLazy类:
@Entity
@Table(name = "USER")
public class UserLazy implements Serializable {
@Id
@GeneratedValue
@Column(name = "USER_ID")
private Long userId;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
private Set<OrderDetail> orderDetail = new HashSet();
// standard setters and getters
// also override equals and hashcode
}
接下来,我们将看到OrderDetail类:
@Entity
@Table (name = "USER_ORDER")
public class OrderDetail implements Serializable {
@Id
@GeneratedValue
@Column(name="ORDER_ID")
private Long orderId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name="USER_ID")
private UserLazy user;
// standard setters and getters
// also override equals and hashcode
}
一个User可以有多个OrderDetails。在Eager加载策略中,如果我们加载User数据,它也会加载与其关联的所有订单并将其存储在内存中。
但是当我们启用Lazy加载时,如果我们拉起一个UserLazy,OrderDetail数据将不会被初始化并加载到内存中,直到我们显式调用它。
在下一节中,我们将看到如何在 Hibernate 中实现该示例。
4. 加载配置
让我们看看如何在 Hibernate 中配置抓取策略。
我们可以使用这个注解参数来启用Lazy加载:
fetch = FetchType.LAZY
对于 Eager Fetching,我们使用这个参数:
fetch = FetchType.EAGER
为了设置 Eager 加载,我们使用了UserLazy的名为UserEager的孪生类。
在下一节中,我们将看看这两种获取类型之间的区别。
5. 差异
正如我们所提到的,这两种获取方式的主要区别在于数据加载到内存中的时间。
我们来看一下:
List<UserLazy> users = sessionLazy.createQuery("From UserLazy").list();
UserLazy userLazyLoaded = users.get(3);
return (userLazyLoaded.getOrderDetail());
使用惰性初始化方法,orderDetailSet只有在我们使用 getter 或其他方法显式调用它时才会被初始化:
UserLazy userLazyLoaded = users.get(3);
但是通过UserEager中的 eager 方法,它将在第一行立即初始化:
List<UserEager> user = sessionEager.createQuery("From UserEager").list();
对于Lazy加载,我们使用代理对象并触发单独的 SQL 查询来加载orderDetailSet。
禁用代理或Lazy加载的想法在 Hibernate 中被认为是一种不好的做法。它可能导致获取和存储大量数据,无论是否需要。
我们可以使用以下方法来测试功能:
Hibernate.isInitialized(orderDetailSet);
现在让我们看看在这两种情况下生成的查询:
<property name="show_sql">true</property>
fetching.hbm.xml中的上述设置显示了生成的 SQL 查询。如果我们查看控制台输出,我们可以看到生成的查询。
对于Lazy加载,这是为加载User数据而生成的查询:
select user0_.USER_ID as USER_ID1_0_, ... from USER user0_
然而,在Eager加载中,我们看到了一个使用USER_ORDER进行的连接:
select orderdetai0_.USER_ID as USER_ID4_0_0_, orderdetai0_.ORDER_ID as ORDER_ID1_1_0_, orderdetai0_ ...
from USER_ORDER orderdetai0_ where orderdetai0_.USER_ID=?
上面的查询是为所有User生成的,这导致比其他方法更多的内存使用。
6. 优缺点
6.1. Lazy加载
优点:
- 比其他方法更短的初始加载时间
- 与其他方法相比,内存消耗更少
缺点:
- Lazy初始化可能会在不需要的时刻影响性能。
- 在某些情况下,我们需要特别小心地处理Lazy初始化的对象,否则我们可能会遇到异常。
6.2. Eager加载
优点:
- 没有Lazy初始化相关的性能影响
缺点:
- 初始加载时间长
- 加载过多不必要的数据可能会影响性能
7. Hibernate 中的Lazy加载
Hibernate 通过提供类的代理实现对实体和关联应用Lazy加载方法。
Hibernate 通过用派生自实体类的代理替换实体来拦截对实体的调用。在我们的示例中,在将控制权交给User类实现之前,将从数据库中加载缺少的请求信息。
我们还应该注意,当关联表示为集合类(在上面的示例中,它表示为Set<OrderDetail> orderDetailSet)时,会创建一个包装器并将其替换为原始集合。
要了解有关代理设计模式的更多信息,请参阅此处 。