Eager/Lazy Loading In Hibernate
1. Introdução
Ao trabalhar com um ORM, a busca / carregamento de dados pode ser classificada em dois tipos: ansioso e preguiçoso.
Neste artigo rápido, mostraremos as diferenças e mostraremos que elas podem ser usadas no Hibernate.
2. Dependências do Maven
Para usar o Hibernate, vamos primeiro definir a dependência principal em nossopom.xml:
org.hibernate
hibernate-core
5.2.2.Final
A última versão do Hibernate pode ser encontradahere.
3. Carregamento ansioso e preguiçoso
A primeira coisa que devemos discutir aqui é o que carregamento lento e carregamento ansioso são:
-
Eager Loading é um padrão de design no qual a inicialização de dados ocorre no local
-
Lazy Loading é um padrão de design que é usado para adiar a inicialização de um objeto, contanto que seja possível
Vamos ver como isso realmente funciona com alguns exemplos:
A classeUserLazy:
@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 = new HashSet();
// standard setters and getters
// also override equals and hashcode
}
A classeOrderDetail:
@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
}
UmUser pode ter váriosOrderDetails. 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.
Mas, quando o carregamento lento está ativado, se extrairmos umUserLazy, os dados deOrderDetail não serão inicializados e carregados em uma memória até que uma chamada explícita seja feita a eles.
Na próxima seção, veremos como o exemplo acima é implementado no Hibernate.
4. Carregando configuração
Nesta seção, veremos como podemos configurar estratégias de busca no Hibernate. Reutilizaremos exemplos da seção anterior.
O carregamento lento pode ser simplesmente ativado usando o seguinte parâmetro de anotação:
fetch = FetchType.LAZY
Para usar o Eager Fetching, o seguinte parâmetro é usado:
fetch = FetchType.EAGER
Para configurar o Eager Loading, usamos a classe gêmea deUserLazy chamadaUserEager.
Na próxima seção, veremos as diferenças entre os dois tipos de busca.
5. Diferenças
Como mencionamos, a principal diferença entre os dois tipos de busca é um momento em que os dados são carregados na memória.
Vejamos este exemplo:
List users = sessionLazy.createQuery("From UserLazy").list();
UserLazy userLazyLoaded = users.get(3);
return (userLazyLoaded.getOrderDetail());
Com a abordagem de inicialização lenta,orderDetailSet será inicializado apenas quando for explicitamente chamado usando um getter ou algum outro método, conforme mostrado no exemplo acima:
UserLazy userLazyLoaded = users.get(3);
Mas com uma abordagem rápida emUserEager, ele será inicializado imediatamente na primeira linha do exemplo acima:
List user = sessionEager.createQuery("From UserEager").list();
Para carregamento lento, um objeto proxy é usado e uma consulta SQL separada é disparada para carregar oorderDetailSet.
A idéia de desativar proxies ou carregamento lento é considerada uma prática ruim no Hibernate. Isso pode resultar em muitos dados sendo buscados em um banco de dados e armazenados na memória, independentemente da necessidade.
O método a seguir pode ser usado para testar a funcionalidade acima:
Hibernate.isInitialized(orderDetailSet);
Agora é importante dar uma olhada nas consultas que são geradas nos dois casos:
true
A configuração acima emfetching.hbm.xml mostra as consultas SQL que são geradas. Se você observar uma saída do console, poderá ver as consultas geradas.
Para Lazy Loading, a consulta gerada para carregar os dadosUser:
select user0_.USER_ID as USER_ID1_0_, ... from USER user0_
No entanto, no carregamento antecipado, vimos uma junção sendo feita comUSER_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=?
A consulta acima é gerada para todos osUsers, o que resulta em muito mais memória sendo usada do que na outra abordagem.
6. Vantagens e desvantagens
6.1. Carregamento lento
Vantagens:
-
Tempo de carregamento inicial muito menor do que na outra abordagem
-
Menos consumo de memória do que na outra abordagem
Desvantagens:
-
A inicialização atrasada pode afetar o desempenho durante momentos indesejados
-
Em alguns casos, você precisa manipular objetos inicializados preguiçosamente com um cuidado especial ou pode acabar com uma exceção
6.2. Carregamento ansioso:
Vantagens:
-
Não há impactos no desempenho relacionados à inicialização atrasada
Desvantagens:
-
Tempo inicial de carregamento longo
-
Carregar muitos dados desnecessários pode afetar o desempenho
7. Carregamento lento em hibernação
Hibernate applies lazy loading approach on entities and associations by providing a proxy implementation de classes.
O Hibernate intercepta chamadas para uma entidade, substituindo-o por um proxy derivado da classe de uma entidade. Em nosso exemplo, quando uma informação solicitada está faltando, ela será carregada de um banco de dados antes que o controle seja cedido para a implementação da classeUser.
Também deve ser observado que quando a associação é representada como uma classe de coleção (nos exemplos acima ela é representada comoSet<OrderDetail> orderDetailSet), então um wrapper é criado e substituído por uma coleção original.
Para saber mais sobre o padrão de design de proxy, você pode consultarhere.
8. Conclusão
Neste artigo, mostramos os exemplos dos dois principais tipos de busca usados no Hibernate.
Para conhecimento de nível avançado, você pode consultar o site oficial do Hibernate. Para obter o código discutido neste artigo, dê uma olhada nesterepository.