Eager/Lazy Loading In Hibernate – Hibernate中的急迫/懒惰加载

最后修改: 2016年 8月 19日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

When working with an ORM, data fetching/loading can be classified into two types: eager and lazy.

在使用ORM时,数据的获取/加载可以分为两种类型:急切和懒惰。

In this quick tutorial, we are going to point out differences and show how we can use these in Hibernate.

在这个快速教程中,我们将指出差异,并展示我们如何在Hibernate中使用这些。

2. Maven Dependencies

2.Maven的依赖性

In order to use Hibernate, let’s first define the main dependency in our pom.xml:

为了使用Hibernate,首先让我们在pom.xml中定义主要的依赖关系。

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>   
    <version>5.2.2.Final</version>
</dependency>

The latest version of Hibernate can be found here.

最新版本的Hibernate可以在这里找到。

3. Eager and Lazy Loading

3.急切和懒惰的加载

The first thing that we should discuss here is what lazy loading and eager loading are:

我们在这里应该讨论的第一件事是什么是懒惰加载和急切加载。

  • Eager Loading is a design pattern in which data initialization occurs on the spot.
  • Lazy Loading is a design pattern that we use to defer initialization of an object as long as it’s possible.

Let’s see how this works.

让我们看看这是如何运作的。

First, we’ll look at the UserLazy class:

首先,我们来看看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

}

Next, we’ll see the OrderDetail class:

接下来,我们将看到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

}

One User can have multiple OrderDetails. In eager loading strategy, if we load the User data, it will also load up all orders associated with it and will store it in a memory.

一个User可以有多个OrderDetails在急切加载策略中,如果我们加载User数据,它也将加载与之相关的所有订单并将其存储在内存中。

But when we enable lazy loading, if we pull up a UserLazy, OrderDetail data won’t be initialized and loaded into a memory until we make an explicit call to it.

但是当我们启用懒惰加载时,如果我们拉起一个UserLazyOrderDetail数据将不会被初始化并加载到一个内存中,直到我们对它进行显式调用。

In the next section, we’ll see how we implement the example in Hibernate.

在下一节中,我们将看到我们如何在Hibernate中实现这个例子。

4. Loading Configuration

4.装载配置

Let’s look at how to configure fetching strategies in Hibernate.

让我们来看看如何在Hibernate中配置获取策略。

We can enable Lazy Loading by using this annotation parameter:

我们可以通过使用这个注解参数来启用懒惰加载。

fetch = FetchType.LAZY

For Eager Fetching, we use this parameter:

对于Eager Fetching,我们使用这个参数。

fetch = FetchType.EAGER

To set up Eager Loading, we have used UserLazy‘s twin class called UserEager.

为了设置Eager Loading,我们使用了UserLazy的孪生类,称为UserEager

In the next section, we will look at the differences between the two types of fetching.

在下一节中,我们将看一下这两种获取方式的区别。

5. Differences

5.差异

As we mentioned, the main difference between the two types of fetching is the moment when data gets loaded into a memory.

正如我们所提到的,这两种获取方式的主要区别在于数据被加载到内存的时刻。

Let’s have a look:

让我们看一看。

List<UserLazy> users = sessionLazy.createQuery("From UserLazy").list();
UserLazy userLazyLoaded = users.get(3);
return (userLazyLoaded.getOrderDetail());

With the lazy initialization approach, orderDetailSet will get initialized only when we explicitly call it, using a getter or some other method:

使用懒人初始化方法,orderDetailSet只有在我们使用getter或其他方法明确调用它时才会被初始化。

UserLazy userLazyLoaded = users.get(3);

But with an eager approach in UserEager, it will be initialized immediately in the first line:

但在UserEager中采用了急切的方法,它将在第一行立即被初始化。

List<UserEager> user = sessionEager.createQuery("From UserEager").list();

For lazy loading, we use a proxy object and fire a separate SQL query to load the orderDetailSet.

对于懒惰加载,我们使用一个代理对象并启动一个单独的SQL查询来加载orderDetailSet

The idea of disabling proxies or lazy loading is considered a bad practice in Hibernate. It can result in fetching and storing a lot of data, irrespective of the need for it.

在Hibernate中,禁用代理或懒惰加载的想法被认为是一种不好的做法。它可能会导致获取和存储大量的数据,而不考虑对它的需求。

We can use the following method to test the functionality:

我们可以使用以下方法来测试功能。

Hibernate.isInitialized(orderDetailSet);

Now let’s have a look at the queries generated in either case:

现在让我们来看看这两种情况下产生的查询。

<property name="show_sql">true</property>

The above setting in the fetching.hbm.xml shows the generated SQL queries. If we look at a console output, we can see generated queries.

fetching.hbm.xml中的上述设置显示了生成的SQL查询。如果我们看一下控制台输出,我们可以看到生成的查询。

For Lazy Loading, here’s the query generated to load the User data:

对于Lazy Loading,这里是为加载User数据而生成的查询。

select user0_.USER_ID as USER_ID1_0_,  ... from USER user0_

However, in eager loading, we saw a join made with USER_ORDER:

然而,在急切的加载中,我们看到用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=?

The above query is generated for all Users, which results in much more memory use than in the other approach.

上述查询是为所有Users生成的,这导致内存的使用比其他方法多得多。

6. Advantages and Disadvantages

6.优势和劣势

6.1. Lazy Loading

6.1.懒惰加载

Advantages:

优势。

  • Much smaller initial load time than in the other approach
  • Less memory consumption than in the other approach

Disadvantages:

劣势。

  • Delayed initialization might impact performance during unwanted moments.
  • In some cases we need to handle lazily initialized objects with special care, or we might end up with an exception.

6.2. Eager Loading

6.2.急于加载

Advantages:

优势。

  • No delayed initialization-related performance impacts

Disadvantages:

劣势。

  • Long initial loading time
  • Loading too much unnecessary data might impact performance

7. Lazy Loading in Hibernate

7.Hibernate中的懒惰加载

Hibernate applies lazy loading approach on entities and associations by providing a proxy implementation of classes.

Hibernate通过提供类的代理实现,在实体和关联上应用懒惰加载方法

Hibernate intercepts calls to an entity by substituting it with a proxy derived from an entity’s class. In our example, missing requested information will be loaded from a database before control is ceded to the User class implementation.

Hibernate通过用派生自实体类的代理来拦截对实体的调用。在我们的例子中,在控制权移交给User类实现之前,缺失的请求信息将从数据库加载。

We should also note that when the association is represented as a collection class (in the above examples, it is represented as Set<OrderDetail> orderDetailSet), a wrapper is created and substituted for an original collection.

我们还应该注意到,当关联被表示为一个集合类时(在上面的例子中,它被表示为Set<OrderDetail>orderDetailSet),一个包装器被创建并取代了原始集合。

To know more about proxy design pattern, refer here.

要了解更多关于代理设计模式的信息,请参考这里

8. Conclusion

8.结论

In this article, we showed examples of the two main types of fetching used in Hibernate.

在这篇文章中,我们展示了Hibernate中使用的两种主要获取方式的例子。

For advanced expertise, check the official website of Hibernate.

对于高级的专业知识,请查看Hibernate的官方网站。

To get the code discussed in this article, please have a look at this repository.

要获得本文讨论的代码,请看这个资源库