Differenza tra FetchType LAZY ed EAGER nell'API Java Persistence?


553

Sono un principiante di Java Persistence API e Hibernate.

Qual è la differenza tra FetchType.LAZYe FetchType.EAGERnell'API Java Persistence?


1
Il caricamento EAGER delle raccolte significa che vengono recuperate completamente al momento del recupero del genitore. Durante il caricamento di EAGER, tutto il mio bambino viene recuperato. Il bambino viene recuperato in PersistentSet e PersistentList (o PersistentBag), all'interno del Persistent Bag, viene visualizzato come un elenco di array. È corretto ?? ..
geetha

Risposte:


1065

A volte hai due entità e c'è una relazione tra loro. Ad esempio, potresti avere un'entità chiamata Universitye un'altra entità chiamata Studente un'Università potrebbe avere molti Studenti:

L'entità Università potrebbe avere alcune proprietà di base come ID, nome, indirizzo, ecc. Nonché una proprietà della raccolta chiamata studenti che restituisce l'elenco di studenti per una determinata università:

Un'università ha molti studenti

public class University {
   private String id;
   private String name;
   private String address;
   private List<Student> students;

   // setters and getters
}

Ora quando carichi un'università dal database, JPA carica i suoi campi ID, nome e indirizzo. Ma hai due opzioni per caricare gli studenti:

  1. Per caricarlo insieme al resto dei campi (cioè con entusiasmo), oppure
  2. Per caricarlo su richiesta (pigramente) quando si chiama il getStudents()metodo dell'università .

Quando un'università ha molti studenti non è efficiente caricare tutti i suoi studenti insieme, specialmente quando non sono necessari e in tali casi puoi dichiarare che vuoi che gli studenti vengano caricati quando sono effettivamente necessari. Questo si chiama caricamento lento.

Ecco un esempio, in cui studentsè esplicitamente contrassegnato per essere caricato con entusiasmo:

@Entity
public class University {

    @Id
    private String id;

    private String name;

    private String address;

    @OneToMany(fetch = FetchType.EAGER)
    private List<Student> students;

    // etc.    
}

Ed ecco un esempio in cui studentsè esplicitamente contrassegnato per essere caricato pigramente:

@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 puoi elencare alcune differenze pratiche o i vantaggi e gli svantaggi di ogni tipo di carico (oltre all'efficienza che hai citato). Perché uno dovrebbe voler usare il caricamento desideroso?
ADTC,

73
@ADTC Affinché il caricamento lento funzioni, la sessione JDBC deve essere ancora aperta quando le entità di destinazione vogliono essere caricate in memoria invocando il metodo getter (ad esempio getStudents()), ma a volte ciò non è possibile, perché al momento questo metodo viene chiamato, la sessione è già chiusa e l'entità rimossa. Allo stesso modo, a volte abbiamo un'architettura client / server (ad esempio client Swing / server JEE) e le entità / DTO vengono trasferite via cavo al client e, ancora una volta, molto spesso in questi scenari il caricamento lento non funziona a causa del modo in cui le entità sono serializzati sul filo.
TheFooProgrammer,

4
Vorrei aggiungere ulteriori informazioni a questa risposta dal mio libro - Per risparmiare memoria, il caricamento pigro viene generalmente utilizzato per relazioni da una a molte e molte a molte. Per uno a uno, generalmente si utilizza Eager.
Erran Morad

2
Nel caricamento lento, quando chiamo il getStudents()metodo per la prima volta, i risultati vengono memorizzati nella cache? in modo da poter accedere a quei risultati più velocemente la prossima volta?
JavaTechnical

2
@JavaTechnical dipende se abiliti la cache di secondo livello (abilitata per impostazione predefinita)
Ced

285

Fondamentalmente,

LAZY = fetch when needed
EAGER = fetch immediately

11
Molto chiaro ma solo dopo aver letto la risposta di @ Behang. Grazie per un chiaro riepilogo. :-)
Nabin,

66

EAGERil caricamento delle raccolte significa che vengono recuperate completamente al momento del recupero del loro genitore. Quindi, se hai Coursee ha List<Student>, tutti gli studenti vengono recuperati dal database al momento delCourse recupero.

LAZYd'altra parte significa che il contenuto del file Listviene recuperato solo quando si tenta di accedervi. Ad esempio, chiamando course.getStudents().iterator(). La chiamata a qualsiasi metodo di accesso su Listavvia una chiamata al database per recuperare gli elementi. Questo viene implementato creando un proxy attorno al List(o Set). Quindi per le tue collezioni pigre, i tipi concreti non sono ArrayListe HashSet, ma PersistentSete PersistentList(o PersistentBag)


Ho usato questo concetto per recuperare i dettagli di un'entità figlio, ma non riesco a vedere alcuna differenza tra loro. Quando specifico il recupero Eager, recupera tutto e quando eseguo il debug, vedo "Bean differito" nell'entità figlio. Quando dico course.getStudents(), genera una query SQL (vista sulla console). Anche nel tipo di recupero pigro, accade la stessa cosa. Quindi, qual è la differenza ??
Neha Choudhary,

le raccolte desiderose vengono recuperate quando viene caricata l'entità proprietaria. Le raccolte pigre vengono recuperate quando accedi ad esse. Se questo non è il comportamento che hai visto, probabilmente c'era qualcosa che non andava nel tuo ambiente (es
Eseguire

1
@Bozho Hai specificato il caricamento lento delle raccolte. È possibile caricare lentamente un campo stringa semplice?
vikiiii,

No. È necessario utilizzare una query o un'entità mappata diversa per ottenere un sottoinsieme delle colonne
Bozho,

@Bozho, ehi, per favore, puoi rispondere a questo, quindi se è impostato su fetchtype = LAZYquello predefinito anche se provi a ottenere la raccolta con il getter hibernete genera un errore che mi dice che non può valutare
Все Едно

16

Potrei considerare le prestazioni e l'utilizzo della memoria. Una grande differenza è che la strategia di recupero di EAGER consente di utilizzare un oggetto dati recuperato senza sessione. Perché?
Tutti i dati vengono recuperati quando i dati marcati desiderati nell'oggetto quando la sessione è connessa. Tuttavia, in caso di strategia di caricamento lento, il caricamento contrassegnato dell'oggetto contrassegnato non recupera i dati se la sessione è disconnessa (dopo l' session.close()istruzione). Tutto ciò che può essere fatto dal proxy ibernazione. La strategia desiderosa consente ai dati di essere ancora disponibili dopo la chiusura della sessione.


11

Secondo le mie conoscenze, entrambi i tipi di recupero dipendono dalle tue esigenze.

FetchType.LAZY è su richiesta (ovvero quando abbiamo richiesto i dati).

FetchType.EAGER è immediato (vale a dire prima che arrivi il nostro requisito stiamo recuperando inutilmente il record)


11

Per impostazione predefinita, per tutti gli oggetti raccolta e mappa è la regola di recupero FetchType.LAZYe per altre istanze segue la FetchType.EAGERpolitica.
In breve, @OneToManye le @ManyToManyrelazioni non recuperano implicitamente gli oggetti correlati (raccolta e mappa), ma l'operazione di recupero viene messa in cascata attraverso il campo in @OneToOnee @ManyToOnequelli.

(cortesia: - objectdbcom)


9

Entrambi FetchType.LAZYe FetchType.EAGERvengono utilizzati per definire il piano di recupero predefinito .

Sfortunatamente, puoi solo sostituire il piano di recupero predefinito per il recupero LAZY. Il recupero EAGER è meno flessibile e può portare a molti problemi di prestazioni .

Il mio consiglio è di frenare l'impulso di rendere le vostre associazioni EAGER perché il recupero è una responsabilità del tempo delle query. Quindi tutte le tue query dovrebbero usare la direttiva fetch per recuperare solo ciò che è necessario per il caso aziendale corrente.


2
"Il recupero di EAGER è meno flessibile e può portare a molti problemi di prestazioni." ... Un'affermazione più vera è "L'uso o meno del recupero EAGER può portare a problemi di prestazioni". In quel caso particolare, quando un campo pigramente inizializzato è costoso per accedere E usato di rado, il recupero pigro andrà a beneficio delle prestazioni. Tuttavia, nel caso in cui una variabile venga utilizzata di frequente, l'inizializzazione lenta può effettivamente degradare le prestazioni richiedendo più viaggi nel database rispetto all'inizializzazione desiderosa. Suggerirei di applicare FetchType correttamente, non dogmaticamente.
Scott

Stai promuovendo il tuo libro qui !! Ma sì, penso che dipenda dal caso d'uso e dalla dimensione dell'oggetto a cui si fa riferimento nella relazione cardinalità.
John Doe,

6

Dal Javadoc :

La strategia EAGER è un requisito per il runtime del provider di persistenza che i dati devono essere recuperati con impazienza. La strategia LAZY è un suggerimento per il runtime del provider di persistenza che i dati dovrebbero essere recuperati pigramente al primo accesso.

Ad esempio, desideroso è più proattivo che pigro. Lazy si verifica solo al primo utilizzo (se il provider prende il suggerimento), mentre con cose desiderose (può) essere pre-recuperato.


1
cosa intendi per "primo utilizzo"?
leon

@leon: supponi di avere un'entità con un campo desideroso e un campo pigro. Quando si ottiene l'entità, il campo desideroso sarà stato caricato dal DB quando si riceve il riferimento entità, ma il campo pigro potrebbe non esserlo. Verrà recuperato solo quando si tenta di accedere al campo tramite il suo accessor.
TJ Crowder,

@TJ Crowder, qual è l'impostazione predefinita quando non viene definito nessun tipo di fetch?
Mahmoud Saleh,

@MahmoudSaleh: non ne ho idea. Probabilmente varia in base a qualcosa. Non ho usato JPA in un vero progetto, quindi non me ne sono reso conto.
TJ Crowder,

2
@MahmoudS: Fetchtypes predefiniti: OneToMany: LAZY, ManyToOne: EAGER, ManyToMany: LAZY, OneToOne: EAGER, Colonne: EAGER
Markus Pscheidt,

5

Il Lazytipo di recupero è selezionato per impostazione predefinita da Hibernate a meno che non si contrassegni esplicitamente il Eagertipo di recupero. Per essere più precisi e concisi, la differenza può essere dichiarata come di seguito.

FetchType.LAZY = Questo non carica le relazioni a meno che non venga invocato tramite il metodo getter.

FetchType.EAGER = Questo carica tutte le relazioni.

Pro e contro di questi due tipi di recupero.

Lazy initialization migliora le prestazioni evitando calcoli non necessari e riduce i requisiti di memoria.

Eager initialization richiede più consumo di memoria e la velocità di elaborazione è lenta.

Detto questo, dipende dalla situazione in cui una di queste inizializzazioni può essere utilizzata.


1
L'affermazione che "non carica le relazioni a meno che non venga invocata tramite il metodo getter" è importante da notare, e anche una decisione di progetto piuttosto ritardata a mio avviso ... Ho appena incontrato un caso in cui supponevo che lo avrebbe recuperato all'accesso e no, perché non ho chiamato esplicitamente una funzione getter per questo. A proposito, cosa costituisce una funzione "getter"? JPA rimanderà il caricamento della proprietà fino a quando getMemberviene chiamata una funzione chiamata che corrisponde esattamente al modello di nome del 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();
        }
    }

    }

Controllare il metodo retrieve () di Main.java. Quando otteniamo Oggetto, la sua lista di raccolte Libri , annotata con @OneToMany, verrà caricata pigramente. Ma, d'altra parte, l'associazione relativa all'argomento della raccolta di Libri , annotata con @ManyToOne, viene caricata in anticipo (da [default][1]per @ManyToOne, fetchType=EAGER). È possibile modificare il comportamento posizionando fetchType.EAGER su @OneToManySubject.java o fetchType.LAZY su @ManyToOnein Books.java.


1

public enum FetchType estende java.lang.Enum Definisce le strategie per il recupero dei dati dal database. La strategia EAGER è un requisito per il runtime del provider di persistenza che i dati devono essere recuperati con impazienza. La strategia LAZY è un suggerimento per il runtime del provider di persistenza che i dati dovrebbero essere recuperati pigramente al primo accesso. L'implementazione è autorizzata a recuperare avidamente i dati per i quali è stato specificato il suggerimento di strategia LAZY. Esempio: @Basic (fetch = LAZY) protetto String getName () {return name; }

fonte


1

Voglio aggiungere questa nota a ciò che "Kyung Hwan Min" ha detto sopra.

Supponiamo che tu stia usando Spring Rest con questo semplice architetto:

Controller <-> Servizio <-> Deposito

E si desidera restituire alcuni dati al front-end, se si utilizza FetchType.LAZY, si otterrà un'eccezione dopo aver restituito i dati al metodo del controller poiché la sessione è chiusa nel servizio, quindi ilJSON Mapper Object non poter ottenere i dati.

Esistono tre opzioni comuni per risolvere questo problema, dipende dal design, dalle prestazioni e dallo sviluppatore:

  1. Il più semplice è usare FetchType.EAGER, in modo che la sessione rimanga attiva con il metodo del controller.
  2. Anti-patternSoluzioni , per rendere la sessione attiva fino al termine dell'esecuzione, è un grosso problema di prestazioni nel sistema.
  3. La migliore pratica è utilizzare FetchType.LAZYcon il metodo del convertitore per trasferire i dati da Entitya un altro oggetto dati DTOe inviarli al controller, quindi non ci sono eccezioni se la sessione viene chiusa.


0

@ drop-shadow se stai utilizzando Hibernate, puoi chiamare Hibernate.initialize()quando invochi il getStudents()metodo:

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: Recupera pigramente le entità figlio, ovvero al momento del recupero dell'entità padre, recupera semplicemente il proxy (creato da cglib o qualsiasi altra utilità) delle entità figlio e quando si accede a qualsiasi proprietà dell'entità figlio, viene effettivamente recuperato da ibernazione.

EAGER: recupera le entità figlio insieme al padre.

Per una migliore comprensione, consultare la documentazione di Jboss oppure è possibile utilizzare hibernate.show_sql=trueper la propria app e controllare le query emesse dal letargo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.