Contents

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加载时,如果我们拉起一个UserLazyOrderDetail数据将不会被初始化并加载到内存中,直到我们显式调用它。

在下一节中,我们将看到如何在 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)时,会创建一个包装器并将其替换为原始集合。

要了解有关代理设计模式的更多信息,请参阅此处