Je suis un débutant pour Java Persistence API et Hibernate.
Quelle est la différence entre FetchType.LAZY
et FetchType.EAGER
dans l'API Java Persistence?
Je suis un débutant pour Java Persistence API et Hibernate.
Quelle est la différence entre FetchType.LAZY
et FetchType.EAGER
dans l'API Java Persistence?
Réponses:
Parfois, vous avez deux entités et il y a une relation entre elles. Par exemple, vous pouvez avoir une entité appelée University
et une autre entité appelée Student
et une université peut avoir plusieurs étudiants:
L'entité University peut avoir certaines propriétés de base telles que id, nom, adresse, etc. ainsi qu'une propriété de collection appelée student qui renvoie la liste des étudiants pour une université donnée:
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 ses champs d'ID, de nom et d'adresse pour vous. Mais vous avez deux options pour charger les étudiants:
getStudents()
méthode de l'université .Lorsqu'une université compte de nombreux étudiants, il n'est pas efficace de charger tous ses étudiants avec elle, en particulier lorsqu'ils ne sont pas nécessaires et dans ce cas, vous pouvez déclarer que vous souhaitez que les étudiants soient chargés lorsqu'ils sont réellement nécessaires. C'est ce qu'on appelle le chargement paresseux.
Voici un exemple, où students
est explicitement marqué pour être chargé avec impatience:
@Entity
public class University {
@Id
private String id;
private String name;
private String address;
@OneToMany(fetch = FetchType.EAGER)
private List<Student> students;
// etc.
}
Et voici un exemple où students
est explicitement marqué pour être chargé paresseusement:
@Entity
public class University {
@Id
private String id;
private String name;
private String address;
@OneToMany(fetch = FetchType.LAZY)
private List<Student> students;
// etc.
}
getStudents()
), mais parfois ce n'est pas possible, car au moment où cette méthode est appelée, la session est déjà fermée et l'entité détachée. De même, nous avons parfois une architecture client / serveur (par exemple Swing client / serveur JEE) et les entités / DTO sont transférées via le câble au client et, le plus souvent dans ces scénarios, le chargement paresseux ne fonctionnera pas en raison de la façon dont les entités sont sérialisés sur le fil.
getStudents()
méthode pour la première fois, les résultats sont-ils mis en cache? afin que je puisse accéder à ces résultats plus rapidement la prochaine fois?
EAGER
le chargement des collections signifie qu'elles sont entièrement récupérées au moment où leur parent est récupéré. Donc, si vous l'avez fait Course
et que c'est le cas List<Student>
, tous les étudiants sont extraits de la base de données au moment où ils Course
sont récupérés.
LAZY
d'autre part signifie que le contenu du List
est récupéré uniquement 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 le List
lancera un appel à la base de données pour récupérer les éléments. Ceci est implémenté en créant un proxy autour du List
(ou Set
). Donc pour vos collections paresseuses, les types concrets ne sont pas ArrayList
et HashSet
, mais PersistentSet
et PersistentList
(ou PersistentBag
)
course.getStudents()
, il déclenche une requête SQL (vu cela sur console). Dans le type Lazy fetch également, la même chose se produit. Alors, quelle est la différence ??
fetchtype = LAZY
celui par défaut même si essayez d'obtenir la collection avec le getter hibernete jette une erreur me disant qu'il ne peut pas évaluer
Je peux considérer les performances et l'utilisation de la mémoire. Une grande différence est que la stratégie de récupération EAGER permet d'utiliser un objet de données récupéré sans session. Pourquoi?
Toutes les données sont extraites lorsqu'elles sont marquées avec empressement dans l'objet lorsque la session est connectée. Cependant, en cas de stratégie de chargement différé, l'objet marqué de chargement différé ne récupère pas les données si la session est déconnectée (après l' session.close()
instruction). Tout cela peut être fait par un proxy d'hibernation. Une stratégie désireuse permet aux données d'être toujours disponibles après la fermeture de la session.
Selon ma connaissance, les deux types de récupération dépendent de vos besoins.
FetchType.LAZY
est sur demande (c'est-à-dire lorsque nous avons demandé les données).
FetchType.EAGER
est immédiat (c'est-à-dire avant que notre exigence ne vienne, nous récupérons inutilement l'enregistrement)
Par défaut, pour tous les objets de collection et de mappage, la règle d'extraction est FetchType.LAZY
et pour les autres instances, elle suit la FetchType.EAGER
politique.
En bref, @OneToMany
et les @ManyToMany
relations ne récupèrent pas implicitement les objets associés (collection et carte) mais l'opération de récupération est répercutée en cascade dans le champ in @OneToOne
et @ManyToOne
ones.
Les deux 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 de récupération par défaut pour la récupération paresseuse. La récupération EAGER est moins flexible et peut entraîner de nombreux problèmes de performances .
Mon conseil est de limiter l'envie de rendre vos associations EAGER car aller chercher est une responsabilité en temps de requête. Ainsi, toutes vos requêtes doivent utiliser la directive fetch pour récupérer uniquement ce qui est nécessaire pour l'analyse de rentabilisation actuelle.
Depuis le Javadoc :
La stratégie EAGER est une exigence sur le runtime du fournisseur de persistance que les données doivent être récupérées avec impatience. La stratégie LAZY indique au moteur d'exécution du fournisseur de persistance que les données doivent être récupérées paresseusement lors de leur premier accès.
Par exemple, désireux est plus proactif que paresseux. Lazy ne se produit que lors de la première utilisation (si le fournisseur prend le conseil), alors qu'avec les choses désireuses (peut) être récupérées à l'avance.
Le Lazy
type d'extraction est par défaut sélectionné par Hibernate sauf si vous marquez explicitement le Eager
type d'extraction. Pour être plus précis et concis, la différence peut être indiquée comme ci-dessous.
FetchType.LAZY
= Cela ne charge pas les relations sauf si vous l'invoquez via la méthode getter.
FetchType.EAGER
= Cela charge toutes les relations.
Avantages et inconvénients de ces deux types de récupération.
Lazy initialization
améliore les performances en évitant les calculs inutiles et en réduisant les besoins en mémoire.
Eager initialization
prend plus de mémoire et la vitesse de traitement est lente.
Cela dit, selon la situation, l'une ou l'autre de ces initialisations peut être utilisée.
getMember
soit appelée qui corresponde exactement au modèle de nom du membre?
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 liste de collections Books , annotée avec @OneToMany
, sera chargée paresseusement. Mais, d'autre part, l'association liée au sujet de la collection Books , annotée avec @ManyToOne
, se charge avec précaution (par [default][1]
pour @ManyToOne
, fetchType=EAGER
). Nous pouvons changer le comportement en plaçant fetchType.EAGER sur @OneToMany
Subject.java ou fetchType.LAZY @ManyToOne
dans Books.java.
enum public FetchType étend java.lang.Enum Définit les stratégies de récupération des données de la base de données. La stratégie EAGER est une exigence sur le runtime du fournisseur de persistance que les données doivent être récupérées avec impatience. La stratégie LAZY indique au moteur d'exécution du fournisseur de persistance que les données doivent être récupérées paresseusement lors de leur premier accès. L'implémentation est autorisée à récupérer avec impatience les données pour lesquelles l'indicateur de stratégie LAZY a été spécifié. Exemple: @Basic (fetch = LAZY) protected String getName () {return name; }
Je veux ajouter cette note à ce que "Kyung Hwan Min" a dit ci-dessus.
Supposons que vous utilisez Spring Rest avec cet architecte simple:
Contrôleur <-> Service <-> Référentiel
Et vous souhaitez renvoyer certaines données à l'interface, si vous utilisez FetchType.LAZY
, vous obtiendrez une exception après avoir renvoyé des données à la méthode du contrôleur puisque la session est fermée dans le service afin que le JSON Mapper Object
ne puisse pas obtenir les données.
Il existe trois options courantes pour résoudre ce problème, cela dépend de la conception, des performances et du développeur:
FetchType.EAGER
, pour que la session soit toujours en vie à la méthode du contrôleur.FetchType.LAZY
avec la méthode du convertisseur pour transférer des données d' Entity
un autre objet de données DTO
et les envoyer au contrôleur, il n'y a donc aucune exception si la session est fermée.@ drop-shadow si vous utilisez Hibernate, vous pouvez appeler Hibernate.initialize()
lorsque vous appelez la getStudents()
méthode:
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'est-à-dire 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 enfants et lorsque vous accédez à une propriété de l'entité enfant, il est en fait récupéré par hibernate.
EAGER: il récupère les entités enfants avec le parent.
Pour une meilleure compréhension, consultez la documentation Jboss ou vous pouvez utiliser hibernate.show_sql=true
pour votre application et vérifier les requêtes émises par l'hibernate.