Diferença entre FetchType LAZY e EAGER na Java Persistence API?


552

Eu sou um novato na API Java Persistence e Hibernate.

Qual é a diferença entre FetchType.LAZYe FetchType.EAGERna Java Persistence API?


1
O carregamento de coleções do EAGER significa que elas são buscadas totalmente no momento em que seus pais são buscados. Enquanto o EAGER carrega, todo o meu filho é buscado. A criança é buscada no PersistentSet e PersistentList (ou PersistentBag), dentro do Persistent Bag, exibida como uma Lista de matriz. É correto ?? ..
geetha

Respostas:


1064

Às vezes você tem duas entidades e há um relacionamento entre elas. Por exemplo, você pode ter uma entidade chamada Universitye 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:

Uma universidade tem muitos estudantes

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:

  1. Para carregá-lo junto com o restante dos campos (ou seja, ansiosamente), ou
  2. Para carregá-lo sob demanda (ou seja, preguiçosamente) quando você chama o 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.
}

5
@BehrangSaeedzadeh, você pode listar algumas diferenças práticas ou as vantagens e desvantagens de cada tipo de carregamento (além da eficiência mencionada). Por que alguém iria querer usar carregamento ansioso?
ADTC

73
@ADTC Para que o carregamento lento funcione, a sessão JDBC ainda deve estar aberta quando as entidades de destino quiserem ser carregadas na memória, invocando o método getter (por exemplo 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.
TheFooProgrammer

4
Gostaria de adicionar mais algumas informações a essa resposta do meu livro - Para economizar memória, o carregamento lento é geralmente usado para um para muitos e muitos para muitos relacionamentos. Para um a um, geralmente Ansioso é usado.
Erran Morad

2
No carregamento lento, quando ligo o 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?
JavaTechnical

2
@JavaTechnical depende se você habilitar o cache de segundo nível (ativada por padrão)
Ced

285

Basicamente,

LAZY = fetch when needed
EAGER = fetch immediately

11
Muito claro, mas somente depois de ler a resposta de @ Behang. Obrigado pelo resumo claro. :-)
Nabin

66

EAGERcarregar coleções significa que elas são buscadas completamente no momento em que seus pais são buscados. Portanto, se você tem Coursee tem List<Student>, todos os alunos são buscados no banco de dados no momento em que Coursesão buscados.

LAZYpor 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 Listiniciará 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 ArrayListe HashSet, mas PersistentSete PersistentList(ou PersistentBag)


Eu usei esse conceito para buscar os detalhes de uma entidade filha, mas não vejo diferença entre eles. Quando eu especifico a busca ansiosa, ele busca tudo e quando eu depuro, vejo "Bean adiado" na entidade filho. Quando digo 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?
Neha Choudhary

coleções ansiosas são buscadas quando a entidade proprietária é carregada. Coleções preguiçosas são buscadas quando você as acessa. Se este não é o comportamento de sua serra, provavelmente havia algo errado com seu ambiente (por exemplo rodando versões antigas de uma classe)
Bozho

1
@Bozho Você especificou o carregamento lento de coleções apenas. Um campo simples de string pode ser carregado preguiçosamente?
vikiiii 12/09

Não. Você precisa usar uma consulta ou uma entidade mapeada diferente para obter um subconjunto das colunas
Bozho

@Bozho, ei, você pode responder isso, se estiver definido no fetchtype = LAZYpadrão, mesmo que tente obter a coleção com o getter hibernete emite um erro dizendo que não pode avaliar
115517

16

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.


11

De acordo com meu conhecimento, ambos os tipos de busca dependem de suas necessidades.

FetchType.LAZY está sob demanda (ou seja, quando solicitamos os dados).

FetchType.EAGER é imediato (ou seja, antes de nossa exigência chegar, estamos buscando desnecessariamente o registro)


11

Por padrão, para todos os objetos de coleção e mapa, a regra de busca é FetchType.LAZYe, para outras instâncias, segue a FetchType.EAGERpolítica.
Em resumo, @OneToManye as @ManyToManyrelaçõ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 @OneToOnee @ManyToOneones.

(cortesia: - objectdbcom)


9

Ambos FetchType.LAZYe FetchType.EAGERsã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.


2
"A busca pelo EAGER é menos flexível e pode levar a muitos problemas de desempenho." ... Uma declaração mais verdadeira é "O uso ou não da busca pelo EAGER pode levar a problemas de desempenho". Nesse caso específico, quando um campo inicial lento é caro para acessar E usado com pouca frequência, a busca lenta beneficiará o desempenho. Porém, no caso de uma variável ser usada com frequência, a inicialização lenta pode realmente prejudicar o desempenho , exigindo mais viagens ao banco de dados do que a inicialização rápida. Eu sugiro aplicar o FetchType corretamente, não dogmaticamente.
Scottb 18/05

Você está promovendo seu livro aqui !!. Mas sim, sinto que depende do caso de uso e do tamanho do objeto a que se refere o relacionamento de cardinalidade.
John Doe

6

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.


1
o que você quer dizer com "primeiro uso"?
leon

@ Leon: Digamos que você tenha uma entidade com um campo ansioso e um campo preguiçoso. Quando você obtém a entidade, o campo ansioso será carregado do banco de dados no momento em que você recebe a referência da entidade, mas o campo lento pode não ter sido. Ele seria buscado apenas quando você tentasse acessar o campo por meio de seu acessador.
TJ Crowder

@TJ Crowder, qual é o padrão quando nenhum tipo de busca definido?
Mahmoud Saleh

@MahmoudSaleh: Não faço ideia. Provavelmente varia de acordo com alguma coisa. Eu não usei o JPA em um projeto real, então não entrei em detalhes.
TJ Crowder #

2
@MahmoudS: fetchtypes Padrão: OneToMany: preguiçoso, ManyToOne: EAGER, ManyToMany: preguiçoso, OneToOne: ansiosos, Colunas: EAGER
Markus Pscheidt

5

O Lazytipo de busca é, por padrão, selecionado pelo Hibernate, a menos que você marque explicitamente o Eagertipo 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.


1
A afirmação de que "não carrega os relacionamentos a menos que você o invoque pelo método getter" é importante observar, e também uma decisão de design bastante retardada na minha opinião ... Acabei de encontrar um caso em que presumi que seria buscá-lo ao acessar e não, porque eu não chamei explicitamente uma função getter para isso. A propósito, o que constitui uma função "getter"? O JPA adiará o carregamento da propriedade até que uma função chamada getMemberseja chamada que corresponda exatamente ao padrão de nome do membro?
ToVine

3

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.@ManyToOnefetchType=EAGER@OneToMany@ManyToOne


1

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; }

Fonte


1

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 Objectque os dados não possam ser obtidos.

Há três opções comuns para resolver esse problema, depende do design, desempenho e desenvolvedor:

  1. O mais fácil é usar FetchType.EAGER, para que a sessão ainda esteja ativa no método do controlador.
  2. Soluções antipadrões , para tornar a sessão ativa até o término da execução, é um enorme problema de desempenho no sistema.
  3. A melhor prática é usar o FetchType.LAZYmétodo converter para transferir dados de Entityoutro objeto de dados DTOe enviá-los ao controlador, portanto, não haverá exceção se a sessão for fechada.


0

@ 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;
    }
    //...
}

0

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=trueseu aplicativo e verifique as consultas emitidas pelo hibernate.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.