web-dev-qa-db-fra.com

Différence entre FetchType LAZY et EAGER dans l'API de persistance Java?

Je suis un novice des API Java Persistence et Hibernate.

Quelle est la différence entre FetchType.LAZY et FetchType.EAGER dans l'API Java Persistence?

470
leon

Parfois, vous avez deux entités et il existe une relation entre elles. Par exemple, vous pouvez avoir une entité appelée Université et une autre entité appelée Étudiant.

L'entité Université peut avoir certaines propriétés de base telles que l'identifiant, le nom, l'adresse, etc., ainsi qu'une propriété appelée étudiants:

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

 // setters and getters
}

Désormais, lorsque vous chargez une université à partir de la base de données, JPA charge les champs id, nom et adresse pour vous. Mais vous avez deux options pour les étudiants: le charger avec le reste des champs (c'est-à-dire avec impatience) ou le charger à la demande (par exemple, paresseusement) lorsque vous appelez la méthode getStudents () de l'université.

Quand une université compte de nombreux étudiants, il n’est pas efficace de charger tous ses étudiants quand ils ne sont pas nécessaires. Ainsi, dans de tels cas, vous pouvez déclarer que vous voulez que les étudiants soient chargés quand ils sont réellement nécessaires. Cela s'appelle le chargement paresseux.

908
Behrang

Fondamentalement,

LAZY = fetch when needed
EAGER = fetch immediately
248
unbeli

EAGER Le chargement des collections signifie qu'elles sont complètement extraites au moment de l'extraction de leur parent. Ainsi, si vous avez Course et List<Student>, tous les étudiants sont récupérés de la base de données au moment où Course est extrait.

LAZY signifie d'autre part que le contenu de List n'est extrait que lorsque vous essayez d'y accéder. Par exemple, en appelant course.getStudents().iterator(). L'appel de n'importe quelle méthode d'accès sur List initiera un appel à la base de données pour extraire les éléments. Ceci est implémenté en créant un proxy autour du List (ou Set). Ainsi, pour vos collections paresseuses, les types concrets ne sont pas ArrayList et HashSet, mais PersistentSet et PersistentList (ou PersistentBag)

60
Bozho

Je peux considérer les performances et l'utilisation de la mémoire. Une grande différence est que la stratégie d'extraction d'EAGER permet d'utiliser un objet de données récupéré sans session. Pourquoi?
Toutes les données sont récupérées lorsque les données marquées avec impatience dans l'objet lorsque la session est connectée. Cependant, en cas de stratégie de chargement paresseux, le chargement paresseux de l'objet marqué ne récupère pas les données si la session est déconnectée (après l'instruction session.close()). Tout ce qui peut être fait par proxy hibernate. Une stratégie optimiste permet aux données d'être toujours disponibles après la fermeture de la session.

13
Kyung Hwan Min

Par défaut, pour tous les objets de collection et de carte, la règle d'extraction est FetchType.LAZY et, dans les autres cas, elle est conforme à la stratégie FetchType.EAGER.
En résumé, les relations @OneToMany et @ManyToMany ne récupèrent pas implicitement les objets associés (collection et mappage), mais l'opération de récupération est effectuée en cascade par le champ dans ceux de @OneToOne et @ManyToOne.

(courtoisie: - objectdbcom)

11
babai

Selon mes connaissances, le type de récupération dépend de vos besoins.

FetchType.LAZY est à la demande (c’est-à-dire lorsque nous avons demandé les données).

FetchType.EAGER est immédiat (c’est-à-dire qu’avant que nous n’existions, nous récupérons inutilement l’enregistrement)

10

FetchType.LAZY et FetchType.EAGER sont utilisés pour définir le plan de récupération par défaut .

Malheureusement, vous ne pouvez remplacer que le plan d'extraction par défaut pour l'extraction LAZY. L'extraction EAGER est moins flexible et peut entraîner de nombreux problèmes de performances .

Mon conseil est de limiter l'envie de faire de vos associations EAGER car la récupération est une responsabilité de temps de requête. Toutes vos requêtes doivent donc utiliser la directive fetch pour ne récupérer que ce qui est nécessaire pour l'analyse de rentabilisation en cours.

9
Vlad Mihalcea

De la Javadoc :

La stratégie EAGER impose au moteur d'exécution du fournisseur de persistance que les données soient extraites avec impatience. La stratégie LAZY indique au moteur d'exécution du fournisseur de persistance que les données doivent être extraites paresseusement lors du premier accès.

Par exemple, désireux est plus proactif que paresseux. La paresse ne se produit que lors de la première utilisation (si le fournisseur en tient compte), alors que les éléments désirés (peuvent) être pré-récupérés.

6
T.J. Crowder

Le type d'extraction Lazy est sélectionné par défaut par Hibernate, sauf si vous marquez explicitement le type Eager Fetch. Pour être plus précis et concis, la différence peut être indiquée ci-dessous.

FetchType.LAZY = Ceci ne charge pas les relations à moins que vous ne l'appeliez via la méthode getter.

FetchType.EAGER = Ceci charge toutes les relations.

Avantages et inconvénients de ces deux types d'extraction.

Lazy initialization améliore les performances en évitant les calculs inutiles et en réduisant les besoins en mémoire.

Eager initialization consomme plus de mémoire et la vitesse de traitement est lente.

Cela dit, dépend de la situation l’une ou l’autre de ces initialisations peut être utilisée.

4
Dulith De Costa

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

    }

Vérifiez la méthode retrieve () de Main.Java. Lorsque nous obtenons Subject, sa collection listBooks , annotée avec @OneToMany, sera chargée paresseusement. Mais, d’autre part, l’association de collection sujet liée à Books, annotée avec @ManyToOne, se charge rapidement (par [default][1] pour @ManyToOne, fetchType=EAGER). Nous pouvons changer le comportement en plaçant fetchType.EAGER sur @OneToMany Subject.Java ou fetchType.LAZY sur @ManyToOne dans Books.Java.

3
Deepak

enum public FetchType étend Java.lang.Enum Définit les stratégies d'extraction des données de la base de données. La stratégie EAGER impose au moteur d'exécution du fournisseur de persistance que les données soient extraites avec impatience. La stratégie LAZY indique au moteur d'exécution du fournisseur de persistance que les données doivent être extraites paresseusement lors du premier accès. L'implémentation est autorisée à extraire rapidement les données pour lesquelles l'indice de stratégie LAZY a été spécifié. Exemple: @Basic (fetch = LAZY) chaîne protégée getName () {nom de retour; }

Source

1
Anas Lachheb

Je veux ajouter cette note à ce que "Kyung Hwan Min" a dit ci-dessus.

Supposons que vous utilisiez Spring Rest avec ce simple architecte:

Contrôleur <-> Service <-> Référentiel

Et vous souhaitez renvoyer des données au serveur, si vous utilisez FetchType.LAZY, vous obtiendrez une exception après avoir renvoyé des données à la méthode du contrôleur, car la session est fermée dans le service. Ainsi, le JSON Mapper Object Je ne peux pas obtenir les données.

Il existe trois options courantes pour résoudre ce problème, qui dépendent de la conception, des performances et du développeur:

  1. Le plus simple consiste à utiliser FetchType.EAGER afin que la session reste active sur la méthode du contrôleur.
  2. Anti-patterns solutions, pour rendre la session active jusqu'à la fin de l'exécution, il s'agit d'un énorme problème de performances dans le système.
  3. La meilleure pratique consiste à utiliser FetchType.LAZY avec la méthode du convertisseur pour transférer les données de Entity vers un autre objet de données DTO et les envoyer au contrôleur. Il n'y a donc pas d'exception si la session est fermée.
0
Ebraheem Alrabee'

@ drop-shadow si vous utilisez Hibernate, vous pouvez appeler Hibernate.initialize() lorsque vous appelez la méthode getStudents():

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
Jules Martel

LAZY: Il récupère les entités enfants paresseusement, c.-à-d. Qu'au moment de la récupération de l'entité parent, il récupère simplement le proxy (créé par cglib ou tout autre utilitaire) des entités enfant et lorsque vous accédez à une propriété de l'entité enfant, il est en fait récupéré par hibernate.

EAGER: récupère les entités enfants avec le parent.

Pour une meilleure compréhension, consultez la documentation de Jboss ou vous pouvez utiliser hibernate.show_sql=true pour votre application et consulter les requêtes émises par le hibernate.

0
user1157635