Eu sou um novato na API Java Persistence e Hibernate.
Qual é a diferença entre FetchType.LAZY
e FetchType.EAGER
na Java Persistence API?
Eu sou um novato na API Java Persistence e Hibernate.
Qual é a diferença entre FetchType.LAZY
e FetchType.EAGER
na Java Persistence API?
Respostas:
Às vezes você tem duas entidades e há um relacionamento entre elas. Por exemplo, você pode ter uma entidade chamada University
e outra entidade chamadaStudent
e uma Universidade pode ter muitos Estudantes:
A entidade Universidade pode ter algumas propriedades básicas, como identificação, nome, endereço etc., além de uma propriedade de coleção chamada estudantes que retorna a lista de estudantes de uma determinada universidade:
public class University {
private String id;
private String name;
private String address;
private List<Student> students;
// setters and getters
}
Agora, quando você carrega uma universidade a partir do banco de dados, o JPA carrega seus campos de identificação, nome e endereço para você. Mas você tem duas opções para o carregamento dos alunos:
getStudents()
método da universidade .Quando uma universidade tem muitos alunos, não é eficiente carregar todos os seus alunos, especialmente quando eles não são necessários e, em casos semelhantes, você pode declarar que deseja que os alunos sejam carregados quando realmente forem necessários. Isso é chamado de carregamento lento.
Aqui está um exemplo, onde students
é explicitamente marcado para ser carregado com entusiasmo:
@Entity
public class University {
@Id
private String id;
private String name;
private String address;
@OneToMany(fetch = FetchType.EAGER)
private List<Student> students;
// etc.
}
E aqui está um exemplo em que students
é explicitamente marcado para ser carregado preguiçosamente:
@Entity
public class University {
@Id
private String id;
private String name;
private String address;
@OneToMany(fetch = FetchType.LAZY)
private List<Student> students;
// etc.
}
getStudents()
), mas às vezes isso não é possível, porque no momento esse método é chamado, a sessão já está fechada e a entidade desanexada. Da mesma forma, às vezes temos uma arquitetura cliente / servidor (por exemplo, cliente Swing / servidor JEE) e as entidades / DTOs são transferidas por fio para o cliente e, novamente, mais frequentemente nesses cenários, o carregamento lento não funciona devido à maneira como as entidades são serializados através do fio.
getStudents()
método pela primeira vez, os resultados são armazenados em cache? para que eu possa acessar esses resultados mais rapidamente da próxima vez?
EAGER
carregar coleções significa que elas são buscadas completamente no momento em que seus pais são buscados. Portanto, se você tem Course
e tem List<Student>
, todos os alunos são buscados no banco de dados no momento em que Course
são buscados.
LAZY
por outro lado, significa que o conteúdo do List
é buscado apenas quando você tenta acessá-los. Por exemplo, ligando course.getStudents().iterator()
. Chamar qualquer método de acesso no List
iniciará uma chamada ao banco de dados para recuperar os elementos. Isso é implementado criando um Proxy em torno de List
(ou Set
). Portanto, para suas coleções preguiçosas, os tipos de concreto não são ArrayList
e HashSet
, mas PersistentSet
e PersistentList
(ou PersistentBag
)
course.getStudents()
, ele dispara uma consulta SQL (vi no console). No tipo de busca preguiçosa também, acontece a mesma coisa. Então, qual é a diferença?
fetchtype = LAZY
padrão, mesmo que tente obter a coleção com o getter hibernete emite um erro dizendo que não pode avaliar
Eu posso considerar o desempenho e a utilização de memória. Uma grande diferença é que a estratégia de busca do EAGER permite usar o objeto de dados buscado sem sessão. Por quê?
Todos os dados são buscados quando dados marcados com ansiedade no objeto quando a sessão está conectada. No entanto, no caso de estratégia de carregamento lento, o objeto marcado de carregamento lento não recupera dados se a sessão for desconectada (após a session.close()
instrução). Tudo isso pode ser feito pelo proxy de hibernação. A estratégia ansiosa permite que os dados ainda estejam disponíveis após o encerramento da sessão.
Por padrão, para todos os objetos de coleção e mapa, a regra de busca é FetchType.LAZY
e, para outras instâncias, segue a FetchType.EAGER
política.
Em resumo, @OneToMany
e as @ManyToMany
relações não buscam os objetos relacionados (coleção e mapa) implicitamente, mas a operação de recuperação é em cascata através do campo in @OneToOne
e @ManyToOne
ones.
Ambos FetchType.LAZY
e FetchType.EAGER
são usados para definir o plano de busca padrão .
Infelizmente, você só pode substituir o plano de busca padrão para a busca LAZY. A busca pelo EAGER é menos flexível e pode levar a muitos problemas de desempenho .
Meu conselho é restringir o desejo de tornar suas associações EAGER porque a busca é uma responsabilidade do tempo de consulta. Portanto, todas as suas consultas devem usar a diretiva buscar para recuperar apenas o necessário para o caso de negócios atual.
Do Javadoc :
A estratégia EAGER é um requisito no tempo de execução do provedor de persistência de que os dados devem ser buscados ansiosamente. A estratégia LAZY é uma dica para o tempo de execução do provedor de persistência de que os dados devem ser buscados preguiçosamente quando forem acessados pela primeira vez.
Por exemplo, ansioso é mais proativo que preguiçoso. Preguiçoso só acontece no primeiro uso (se o provedor entender a dica), enquanto que coisas ansiosas (podem) são pré-buscadas.
O Lazy
tipo de busca é, por padrão, selecionado pelo Hibernate, a menos que você marque explicitamente o Eager
tipo de busca. Para ser mais preciso e conciso, a diferença pode ser definida como abaixo.
FetchType.LAZY
= Isso não carrega os relacionamentos, a menos que você o invoque pelo método getter.
FetchType.EAGER
= Isso carrega todos os relacionamentos.
Prós e contras desses dois tipos de busca.
Lazy initialization
melhora o desempenho, evitando cálculos desnecessários e reduzindo os requisitos de memória.
Eager initialization
leva mais consumo de memória e a velocidade de processamento é lenta.
Dito isto, depende da situação, qualquer uma dessas inicializações pode ser usada.
getMember
seja chamada que corresponda exatamente ao padrão de nome do membro?
Book.java
import java.io.Serializable;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@Entity
@Table(name="Books")
public class Books implements Serializable{
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="book_id")
private int id;
@Column(name="book_name")
private String name;
@Column(name="author_name")
private String authorName;
@ManyToOne
Subject subject;
public Subject getSubject() {
return subject;
}
public void setSubject(Subject subject) {
this.subject = subject;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthorName() {
return authorName;
}
public void setAuthorName(String authorName) {
this.authorName = authorName;
}
}
Subject.java
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
@Entity
@Table(name="Subject")
public class Subject implements Serializable{
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="subject_id")
private int id;
@Column(name="subject_name")
private String name;
/**
Observe carefully i have mentioned fetchType.EAGER. By default its is fetchType.LAZY for @OneToMany i have mentioned it but not required. Check the Output by changing it to fetchType.EAGER
*/
@OneToMany(mappedBy="subject",cascade=CascadeType.ALL,fetch=FetchType.LAZY,
orphanRemoval=true)
List<Books> listBooks=new ArrayList<Books>();
public List<Books> getListBooks() {
return listBooks;
}
public void setListBooks(List<Books> listBooks) {
this.listBooks = listBooks;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
HibernateUtil.java
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
public class HibernateUtil {
private static SessionFactory sessionFactory ;
static {
Configuration configuration = new Configuration();
configuration.addAnnotatedClass (Com.OneToMany.Books.class);
configuration.addAnnotatedClass (Com.OneToMany.Subject.class);
configuration.setProperty("connection.driver_class","com.mysql.jdbc.Driver");
configuration.setProperty("hibernate.connection.url", "jdbc:mysql://localhost:3306/hibernate");
configuration.setProperty("hibernate.connection.username", "root");
configuration.setProperty("hibernate.connection.password", "root");
configuration.setProperty("dialect", "org.hibernate.dialect.MySQLDialect");
configuration.setProperty("hibernate.hbm2ddl.auto", "update");
configuration.setProperty("hibernate.show_sql", "true");
configuration.setProperty(" hibernate.connection.pool_size", "10");
configuration.setProperty(" hibernate.cache.use_second_level_cache", "true");
configuration.setProperty(" hibernate.cache.use_query_cache", "true");
configuration.setProperty(" cache.provider_class", "org.hibernate.cache.EhCacheProvider");
configuration.setProperty("hibernate.cache.region.factory_class" ,"org.hibernate.cache.ehcache.EhCacheRegionFactory");
// configuration
StandardServiceRegistryBuilder builder = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties());
sessionFactory = configuration.buildSessionFactory(builder.build());
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}
Main.java
import org.hibernate.Session;
import org.hibernate.SessionFactory;
public class Main {
public static void main(String[] args) {
SessionFactory factory=HibernateUtil.getSessionFactory();
save(factory);
retrieve(factory);
}
private static void retrieve(SessionFactory factory) {
Session session=factory.openSession();
try{
session.getTransaction().begin();
Subject subject=(Subject)session.get(Subject.class, 1);
System.out.println("subject associated collection is loading lazily as @OneToMany is lazy loaded");
Books books=(Books)session.get(Books.class, 1);
System.out.println("books associated collection is loading eagerly as by default @ManyToOne is Eagerly loaded");
/*Books b1=(Books)session.get(Books.class, new Integer(1));
Subject sub=session.get(Subject.class, 1);
sub.getListBooks().remove(b1);
session.save(sub);
session.getTransaction().commit();*/
}catch(Exception e){
e.printStackTrace();
}finally{
session.close();
}
}
private static void save(SessionFactory factory){
Subject subject=new Subject();
subject.setName("C++");
Books books=new Books();
books.setAuthorName("Bala");
books.setName("C++ Book");
books.setSubject(subject);
subject.getListBooks().add(books);
Session session=factory.openSession();
try{
session.beginTransaction();
session.save(subject);
session.getTransaction().commit();
}catch(Exception e){
e.printStackTrace();
}finally{
session.close();
}
}
}
Verifique o método retrieve () do Main.java. Quando obtivermos Subject, seus listBooks de coleção , anotados com @OneToMany
, serão carregados lentamente. Mas, por outro lado, a associação de livros relacionada ao assunto da coleção , anotada com @ManyToOne
, carrega rapidamente (por , [default][1]
para ). Podemos mudar o comportamento colocando fetchType.EAGER em Subject.java ou fetchType.LAZY em Books.java.@ManyToOne
fetchType=EAGER
@OneToMany
@ManyToOne
enumeração pública FetchType estende java.lang.Enum Define estratégias para buscar dados do banco de dados. A estratégia EAGER é um requisito no tempo de execução do provedor de persistência de que os dados devem ser buscados ansiosamente. A estratégia LAZY é uma dica para o tempo de execução do provedor de persistência de que os dados devem ser buscados preguiçosamente quando forem acessados pela primeira vez. É permitido à implementação buscar ansiosamente dados para os quais a dica de estratégia LAZY foi especificada. Exemplo: @Basic (fetch = LAZY) String protegida getName () {nome do retorno; }
Quero acrescentar esta nota ao que "Kyung Hwan Min" disse acima.
Suponha que você esteja usando o Spring Rest com este arquiteto simples:
Controlador <-> Serviço <-> Repositório
E você deseja retornar alguns dados para o front-end, se estiver usando FetchType.LAZY
, receberá uma exceção depois de retornar os dados ao método do controlador, uma vez que a sessão é fechada no Serviço para JSON Mapper Object
que os dados não possam ser obtidos.
Há três opções comuns para resolver esse problema, depende do design, desempenho e desenvolvedor:
FetchType.EAGER
, para que a sessão ainda esteja ativa no método do controlador.FetchType.LAZY
método converter para transferir dados de Entity
outro objeto de dados DTO
e enviá-los ao controlador, portanto, não haverá exceção se a sessão for fechada.@ drop-shadow se você estiver usando o Hibernate, você pode chamar Hibernate.initialize()
quando invocar o getStudents()
método:
Public class UniversityDaoImpl extends GenericDaoHibernate<University, Integer> implements UniversityDao {
//...
@Override
public University get(final Integer id) {
Query query = getQuery("from University u where idUniversity=:id").setParameter("id", id).setMaxResults(1).setFetchSize(1);
University university = (University) query.uniqueResult();
***Hibernate.initialize(university.getStudents());***
return university;
}
//...
}
LAZY: Busca as entidades filhas preguiçosamente, ou seja, no momento da busca da entidade pai, apenas busca o proxy (criado pelo cglib ou qualquer outro utilitário) das entidades filhas e, quando você acessa qualquer propriedade da entidade filha, é realmente buscado por hibernação.
EAGER: busca as entidades filho junto com o pai.
Para um melhor entendimento, vá para a documentação do Jboss ou use o hibernate.show_sql=true
seu aplicativo e verifique as consultas emitidas pelo hibernate.