Generic Top

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE

1. Introduction

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

2. Setting up the Example

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

@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.

@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

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

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

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

Using SELECT indicates that the property should be loaded lazily.

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:

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

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.

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.

3.1. @BatchSize

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

@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.

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.

4. FetchMode.JOIN

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

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

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

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

5. FetchMode.SUBSELECT

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

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

We can only use SUBSELECT with collections.

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:

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

6. FetchMode vs. FetchType

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.

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.

7. Conclusion

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

As always all source code is available on GitHub.

Generic bottom

I just announced the new Learn Spring course, focused on the fundamentals of Spring 5 and Spring Boot 2:

>> CHECK OUT THE COURSE

Leave a Reply

avatar
  Subscribe  
Notify of