Chargement hâtif / paresseux en veille prolongée

Haut de persistance

Je viens d'annoncer le nouveau cours Learn Spring , axé sur les principes de base de Spring 5 et Spring Boot 2:

>> VOIR LE COURS

1. Introduction

Lorsque vous travaillez avec un ORM, la récupération / chargement de données peut être classé en deux types: impatient et paresseux.

Dans cet article rapide, nous allons souligner les différences et montrer que celles-ci peuvent être utilisées dans Hibernate.

2. Dépendances de Maven

Pour utiliser Hibernate, définissons d'abord la dépendance principale dans notre pom.xml :

 org.hibernate hibernate-core 5.2.2.Final 

La dernière version d'Hibernate peut être trouvée ici.

3. Chargement impatient et paresseux

La première chose dont nous devrions discuter ici est ce que sont le chargement paresseux et le chargement hâtif:

  • Eager Loading est un modèle de conception dans lequel l'initialisation des données se produit sur place
  • Le Lazy Loading est un modèle de conception utilisé pour différer l'initialisation d'un objet aussi longtemps que possible

Voyons comment cela fonctionne réellement avec quelques exemples:

La classe UserLazy :

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

La classe OrderDetail :

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

Un utilisateur peut avoir plusieurs OrderDetails . Dans la stratégie de chargement hâtif, si nous chargeons les données utilisateur , il chargera également toutes les commandes qui lui sont associées et les stockera dans une mémoire .

Mais, lorsque le chargement paresseux est activé, si nous récupérons un UserLazy , les données OrderDetail ne seront pas initialisées et chargées dans une mémoire tant qu'un appel explicite ne sera pas effectué.

Dans la section suivante, nous verrons comment l'exemple ci-dessus est implémenté dans Hibernate.

4. Chargement de la configuration

Dans cette section, nous verrons comment configurer des stratégies de récupération dans Hibernate. Nous réutiliserons des exemples de la section précédente.

Le chargement différé peut être simplement activé à l'aide du paramètre d'annotation suivant:

fetch = FetchType.LAZY

Pour utiliser Eager Fetching, le paramètre suivant est utilisé:

fetch = FetchType.EAGER

Pour configurer Désireuse Loading nous avons utilisé UserLazy classe jumelle de appelé UserEager .

Dans la section suivante, nous examinerons les différences entre les deux types de récupération.

5. Différences

Comme nous l'avons mentionné, la principale différence entre les deux types de récupération est le moment où les données sont chargées dans une mémoire.

Jetons un œil à cet exemple:

List users = sessionLazy.createQuery("From UserLazy").list(); UserLazy userLazyLoaded = users.get(3); return (userLazyLoaded.getOrderDetail());

Avec l'approche d'initialisation paresseuse, orderDetailSet sera initialisé uniquement lorsqu'il est explicitement appelé à l'aide d'un getter ou d'une autre méthode, comme indiqué dans l'exemple ci-dessus:

UserLazy userLazyLoaded = users.get(3);

Mais avec une approche impatiente dans UserEager, il sera initialisé immédiatement dans la première ligne de l'exemple ci-dessus:

List user = sessionEager.createQuery("From UserEager").list();

Pour le chargement différé, un objet proxy est utilisé et une requête SQL distincte est déclenchée pour charger le orderDetailSet .

L'idée de désactiver les proxies ou le chargement paresseux est considérée comme une mauvaise pratique dans Hibernate. Cela peut entraîner l'extraction d'un grand nombre de données à partir d'une base de données et le stockage dans une mémoire, quel que soit le besoin.

La méthode suivante peut être utilisée pour tester la fonctionnalité ci-dessus:

Hibernate.isInitialized(orderDetailSet);

Il est maintenant important de jeter un œil aux requêtes générées dans les deux cas:

true

Le paramètre ci-dessus dans le fichier fetching.hbm.xml montre les requêtes SQL générées. Si vous regardez une sortie de console, vous pourrez voir les requêtes générées.

Pour le chargement différé, la requête générée pour charger les données utilisateur :

select user0_.USER_ID as USER_ID1_0_, ... from USER user0_

Cependant, lors d'un chargement impatient, nous avons vu une jointure se faire avec USER_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=?

La requête ci-dessus est générée pour tous les utilisateurs , ce qui entraîne l'utilisation de beaucoup plus de mémoire que dans l'autre approche.

6. Avantages et inconvénients

6.1. Chargement paresseux

Avantages:

  • Temps de chargement initial beaucoup plus court que dans l'autre approche
  • Moins de consommation de mémoire que dans l'autre approche

Disadvantages:

  • Delayed initialization might impact performance during unwanted moments
  • In some cases you need to handle lazily-initialized objects with a special care or you might end up with an exception

6.2. Eager Loading:

Advantages:

  • No delayed initialization related performance impacts

Disadvantages:

  • Long initial loading time
  • Loading too much unnecessary data might impact performance

7. Lazy Loading in Hibernate

Hibernate applies lazy loading approach on entities and associations by providing a proxy implementation of classes.

Hibernate intercepts calls to an entity by substituting it with a proxy derived from an entity’s class. In our example, when a requested information is missing, it will be loaded from a database before control is ceded to the User class implementation.

Il convient également de noter que lorsque l'association est représentée comme une classe de collection (dans les exemples ci-dessus, elle est représentée par Set orderDetailSet ), un wrapper est créé et remplacé par une collection originale.

Pour en savoir plus sur le modèle de conception de proxy, vous pouvez vous référer ici.

8. Conclusion

Dans cet article, nous avons montré des exemples des deux principaux types de récupération utilisés dans Hibernate.

Pour une expertise de niveau avancé, vous pouvez consulter le site officiel d'Hibernate. Pour obtenir le code décrit dans cet article, veuillez consulter ce référentiel.

Fond de persistance

Je viens d'annoncer le nouveau cours Learn Spring , axé sur les principes de base de Spring 5 et Spring Boot 2:

>> VOIR LE COURS