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?
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.
Fondamentalement,
LAZY = fetch when needed
EAGER = fetch immediately
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
)
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.
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
.
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)
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.
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.
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.
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.
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; }
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:
FetchType.EAGER
afin que la session reste active sur la méthode du contrôleur.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.@ 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;
}
//...
}
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.