FetchMode no Spring Data JPA

FetchMode no Spring Data JPA

1. Introdução

Neste breve tutorial, daremos uma olhada em diferentes valores deFetchMode que podemos usar na anotação@org.hibernate.annotations.Fetch.

2. Configurando o exemplo

Como exemplo, usaremos a seguinte entidadeCustomer com apenas duas propriedades - um id e um conjunto de pedidos:

@Entity
public class Customer {

    @Id
    @GeneratedValue
    private Long id;

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

    // getters and setters
}

Além disso, criaremos uma entidadeOrder consistindo em um id, um nome e uma referência aoCustomer.

@Entity
public class Order {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

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

    // getters and setters
}

Em cada uma das próximas seções, buscaremos o cliente no banco de dados e obteremos todos os seus pedidos:

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

3. FetchMode.SELECT

Em nossa entidadeCustomer, anotamos a propriedadeorders com uma anotação@Fetch:

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

Usamos@Fetch para descrever como o Hibernate deve recuperar a propriedade quando procuramosCustomer.

UsarSELECT indica que a propriedade deve ser carregada lentamente.

Isso significa que, para a primeira linha:

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

Não veremos uma junção com a tabela de pedidos:

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

E isso para a próxima linha:

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

Veremos consultas subsequentes para os pedidos relacionados:

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

OHibernate FetchMode.SELECT gera uma consulta separada para cadaOrder que precisa ser carregado.

Em nosso exemplo, isso fornece uma consulta para carregar os clientes e cinco consultas adicionais para carregar a coleção de pedidos.

This is known as the n + 1 select problem. A execução de uma consulta acionarán consultas adicionais.

3.1. @BatchSize

FetchMode.SELECT tem uma anotação de configuração opcional usando a anotação@BatchSize:

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

Hibernate tentará carregar a coleção de pedidos em lotes definidos pelo parâmetrosize.

No nosso exemplo, temos apenas cinco pedidos, portanto, uma consulta é suficiente.

Ainda usaremos a mesma consulta:

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

But it will only be run once. Agora temos apenas duas consultas: uma para carregar oCustomere outra para carregar a coleção de pedidos.

4. FetchMode.JOIN

EnquantoFetchMode.SELECT carrega relações lentamente,FetchMode.JOIN as carrega avidamente, digamos por meio de uma junção:

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

Isso resulta em apenas uma consulta paraCustomer e seusOrders:

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

5. FetchMode.SUBSELECT

Como a propriedadeorders é uma coleção, também podemos usarFetchMode.SUBSELECT:

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

Só podemos usarSUBSELECT com coleções.

Com esta configuração, voltamos a uma consulta para oCustomer:

Hibernate:
    select ...
    from customer customer0_

E uma consulta paraOrders, usando uma sub-seleção desta vez:

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

6. FetchMode vs. FetchType

Em geral,FetchMode define comoHibernate buscará os dados (por seleção, junção ou subseleção). FetchType, por outro lado, define se o Hibernate carregará os dados avidamente ou lentamente.

As regras exatas entre esses dois são as seguintes:

  • [.comment-copy] #se o código não defineFetchMode, o padrão éJOINeFetchType funciona conforme definido #

  • comFetchMode.SELECT ouFetchMode.SUBSELECT definido,FetchType também funciona conforme definido

  • comFetchMode.JOIN definido,FetchType é ignorado e uma consulta está sempre ansiosa

Para obter mais informações, consulteEager/Lazy Loading In Hibernate.

7. Conclusão

Neste tutorial, aprendemos sobre os diferentes valores deFetchMode e também como eles estão relacionados aFetchType.

Como sempre, todo o código-fonte está disponível emGitHub.