Lazy/Eager - Carregamento de Entidades utilizando JPA com SpringBoot

rs_marinheiro

Rafael

Posted on May 11, 2022

Lazy/Eager - Carregamento de Entidades utilizando JPA com SpringBoot

Um dos pontos mais importante na construção de um software é a performance, portanto entender as queries e otimizá-las ao máximo que puder pode resultar num belo ganho de desempenho.

Partindo dessa premissa e por mais que utilizamos frameworks como SpringBoot que otimizam todo esse processo entender o funcionamento do lazy e do eager da JPA(Java Persistence API) é fundamental para o desempenho de um APP, pois o uso de maneira errada pode resultar em grandes perdas de performance e forçar a aplicação a realizar diversas queries desnecessárias no banco de dados (podendo ocasionar assim o tão famoso problema das N+1 consultas).

Lazy - Carregamento Preguiçoso

Vamos começar nossos estudos entendendo o lazy que é o carregamento default da JPA quando você não especifica nada. O lazy não faz o carregamento de determinados objetos até que você precise deles(daí vem a tradução de preguiçoso) ele ocorre quando temos entidades associadas para muitos isto é quando temos algum relacionamento OneToMany na nossa entidade.

Eager - Carregamento Ansioso/Guloso

Oposto ao Lazy o eager ja traz do banco os objetos mesmo que você não vá utiliza-los, ele ocorre quando temos entidade associadas para um, isto é quando temos algum relacionamento ManyToOne na nossa entidade.

Exemplificando

Vamos supor que no meu domínio de negócio eu tenho duas classes que se relacionam da seguinte maneira:

Image description

Reparem que no exemplo acima eu tenho um relacionamento um-para-muitos, já que um team pode conter muitos palyers e muitos players podem pertencer a um time.

Pelo padrão da JPA quando eu busco um Player a JPA já traz do banco o team ao qual ele pertence(Eager), já quando eu busco o team a JPA não traz do banco os jogadores pertencentes ao time (Lazy)

Vendo o Funcionamento na Pratica o Funcionamento do Eager

Para vermos como isso ocorre na pratica construí uma API com base no modelo apresentado acima(o link do projeto no GitHub estará ao final do artigo).

Na minha API tenho as seguinte classes mapeadas:
Image description

Image description

No pacote Resource da minha API eu criei um controller com a seguinte configuração:

Image description

Reparem que nesse controller eu chamo um service que irá buscar os dados do player pelo id e do resultado jogo num DTO que eu chamei PlayerDTO, portanto quando faço pelo browser/Postman/Insonia a seguinte chamada (localhost:8080/player/1) a API retorna os seguintes dados do player:

Image description

Bem se habilitarmos o sql lá no nosso application.properties, para vermos as queries que a API faz lá no console veremos o seguinte detalhe:

Image description

Vejam que mesmo não precisando dos dados do team a JPA faz um join (desnecessário nesse caso já que eu só quero os dados do player) na minha tabela team conforme destacado em amarelo, já que como vimos acima por padrão toda vez que a entidade tem uma associação para um o carregamento e eager.

Agora você deve se está se perguntando então como eu faço para customizar essa busca, isto é quero evitar esse join desnecessário?

Bem uma forma e você definir um fetch na mão(muita cautela ao fazer isso pois você pode estar piorando a performance em outros endpoints dependendo do contexto do negócio) da seguinte maneira:

Image description

Ao rodarmos a consulta novamente veremos na console a seguinte query:

Image description

Reparem que desta vez ele não fez join la na minha tabela team e tudo funcionou de boa.

Vendo o Funcionamento na Prática do Lazy

Continuando no estudo da nossa API vamos vê agora o funcionamento do Lazy, para isto no meu pacote resource criei o seguinte controller

Image description

Reparem que nesse controller eu chamo um service que irá buscar os dados do team pelo id e do resultado jogo num DTO que eu chamei TeamDTO, portanto quando faço pelo browser/Postman/Insonia a seguinte chamada (localhost:8080/team/1) a API retorna os seguintes dados do team:

Image description

Agora se dermos uma olhada novamente no console veremos as seguintes queries:

Image description

Reparem que quando executamos essa chamada o JPA faz duas queries na primeira ele busca os dados do player e na segunda de team.
Bem agora você deve esta se fazendo uma outra pergunta, Ué mas como ele fez essa mágica?

Isso só ocorreu pois lá no TeamService eu faço a seguinte chamada conforme vemos abaixo:

result.get().getPlayers()

O getPlayers() que usei disparou outra consulta no banco para buscar os dados do player. Portanto conforme vimos acima por padrão toda vez que a entidade tem uma associação para muitos o carregamento é Lazy(preguiçoso) a JPA não traz o objeto até que você precise dele.

Considerações Finais

O objetivo deste artigo foi justamente apresentar aos leitores os conceitos do Fetch/Eager tão utilizados na JPA. O bom entendimento desses conceitos pode acarretar em ganhos de performance na sua aplicação desde que você adote a melhor prática baseado no seu modelo de negócio.

Links/Referencias Bibliográficas

Repositório do Git da demo: https://github.com/rmarinheiro/demolazyeager

https://www.baeldung.com/hibernate-lazy-eager-loading

💖 💪 🙅 🚩
rs_marinheiro
Rafael

Posted on May 11, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related