FetchMode in Hibernate – Hibernate中的FetchMode

最后修改: 2019年 11月 7日

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

1. Introduction

1.绪论

In this short tutorial, we’ll take a look at different FetchMode values we can use in the @org.hibernate.annotations.Fetch annotation.

在这个简短的教程中,我们将看看我们可以在@org.hibernate.annotations.Fetch注解中使用的不同FetchMode值。

2. Setting up the Example

2.设置实例

As an example, we’ll use the following Customer entity with just two properties – an id and a set of orders:

作为一个例子,我们将使用以下Customer实体,它只有两个属性–一个id和一组订单。

@Entity
public class Customer {

    @Id
    @GeneratedValue
    private Long id;

    @OneToMany(mappedBy = "customer")
    @Fetch(value = FetchMode.SELECT)
    private Set<Order> orders = new HashSet<>();

    // getters and setters
}

Also, we’ll create an Order entity consisting of an id, a name and a reference to the Customer.

此外,我们将创建一个Order实体,由一个id、一个名称和一个对Customer的引用组成。

@Entity
public class Order {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    @ManyToOne
    @JoinColumn(name = "customer_id")
    private Customer customer;

    // getters and setters
}

In each of the next sections, we’ll fetch the customer from the database and get all of its orders:

在接下来的每一节中,我们将从数据库中获取客户并获得其所有的订单。

Customer customer = customerRepository.findById(id).get();
Set<Order> orders = customer.getOrders();

3. FetchMode.SELECT

3. FetchMode.SELECT

On our Customer entity, we’ve annotated the orders property with a @Fetch annotation:

在我们的 Customer 实体上,我们用 @Fetch 注释了 orders 属性。

@OneToMany
@Fetch(FetchMode.SELECT)
private Set<Orders> orders;

We use @Fetch to describe how Hibernate should retrieve the property when we lookup a Customer.

我们使用@Fetch来描述当我们查找Customer.时,Hibernate应该如何检索属性。

Using SELECT indicates that the property should be loaded lazily.

使用SELECT表示该属性应被懒散地加载。

This means that for the first line:

这意味着,对于第一行。

Customer customer = customerRepository.findById(id).get();

We won’t see a join with the orders table:

我们不会看到与订单表的连接。

Hibernate: 
    select ...from customer
    where customer0_.id=?

And that for the next line:

而这是为了下一句话。

Set<Order> orders = customer.getOrders();

We’ll see subsequent queries for the related orders:

我们将看到相关订单的后续查询。

Hibernate: 
    select ...from order
    where order0_.customer_id=?

The Hibernate FetchMode.SELECT generates a separate query for each Order that needs to be loaded.

Hibernate FetchMode.SELECT为每个需要加载的Order生成一个单独的查询。

In our example, that gives one query to load the Customers and five additional queries to load the orders collection.

在我们的例子中,有一个查询用来加载客户,另外有五个查询用来加载订单集合。

This is known as the n + 1 select problem. Executing one query will trigger n additional queries.

这被称为n+1选择问题。执行一个查询将触发n额外的查询。

3.1. @BatchSize

3.1.@BatchSize

FetchMode.SELECT has an optional configuration annotation using the @BatchSize annotation:

FetchMode.SELECT有一个可选的配置注解,使用@BatchSize注解。

@OneToMany
@Fetch(FetchMode.SELECT)
@BatchSize(size=10)
private Set<Orders> orders;

Hibernate will try to load the orders collection in batches defined by the size parameter.

Hibernate将尝试分批加载由size参数定义的订单集合。

In our example, we have just five orders so one query is enough.

在我们的例子中,我们只有五个订单,所以一个查询就足够了。

We’ll still use the same query:

我们仍将使用相同的查询。

Hibernate:
    select ...from order
    where order0_.customer_id=?

But it will only be run once. Now we have just two queries: One to load the Customer and one to load the orders collection.

但它只会被运行一次。现在我们只有两个查询。一个用于加载Customer,一个用于加载orders集合。

4. FetchMode.JOIN

4.FetchMode.JOIN

While FetchMode.SELECT loads relations lazily, FetchMode.JOIN loads them eagerly, say via a join:

FetchMode.SELECT懒散地加载关系时,FetchMode.JOIN急切地加载它们,例如通过一个连接。

@OneToMany
@Fetch(FetchMode.JOIN)
private Set<Orders> orders;

This results in just one query for both the Customer and their Orders:

这导致对Customer和他们的Orders都只有一个查询。

Hibernate: 
    select ...
    from
        customer customer0_ 
    left outer join
        order order1 
            on customer.id=order.customer_id 
    where
        customer.id=?

5. FetchMode.SUBSELECT

5. FetchMode.SUBSELECT

Because the orders property is a collection, we could also use FetchMode.SUBSELECT:

因为orders属性是一个集合,我们也可以使用FetchMode.SUBSELECT

@OneToMany
@Fetch(FetchMode.SUBSELECT)
private Set<Orders> orders;

We can only use SUBSELECT with collections.

我们只能对集合使用SUBSELECT

With this setup, we go back to one query for the Customer:

有了这个设置,我们回到了对客户:的一个查询。

Hibernate: 
    select ...
    from customer customer0_

And one query for the Orders, using a sub-select this time:

还有一次对Orders的查询,这次使用的是子选择。

Hibernate: 
    select ...
    from
        order order0_ 
    where
        order0_.customer_id in (
            select
                customer0_.id 
            from
                customer customer0_
        )

6. FetchMode vs. FetchType

6.FetchModeFetchType

In general, FetchMode defines how Hibernate will fetch the data (by select, join or subselect). FetchType, on the other hand, defines whether Hibernate will load data eagerly or lazily.

一般来说,FetchMode定义了Hibernate将如何获取数据(通过选择、连接或子选择)。另一方面,FetchType定义了Hibernate是急于加载数据还是懒于加载数据。

The exact rules between these two are as follows:

这两者之间的确切规则如下:

  • if the code doesn’t set FetchMode, the default one is JOIN and FetchType works as defined
  • with FetchMode.SELECT or FetchMode.SUBSELECT set, FetchType also works as defined
  • with FetchMode.JOIN set, FetchType is ignored and a query is always eager

For further information please refer to Eager/Lazy Loading In Hibernate.

有关进一步的信息,请参考Hibernate中的紧急/懒惰加载

7. Conclusion

7.结语

In this tutorial, we’ve learned about FetchMode‘s different values and also how they are related to FetchType.

在本教程中,我们已经了解了FetchMode的不同值,以及它们与FetchType的关系。

As always all source code is available over on GitHub.

像往常一样,所有的源代码都可以在GitHub上找到