Sono un principiante di Java Persistence API e Hibernate.
Qual è la differenza tra FetchType.LAZY
e FetchType.EAGER
nell'API Java Persistence?
Sono un principiante di Java Persistence API e Hibernate.
Qual è la differenza tra FetchType.LAZY
e FetchType.EAGER
nell'API Java Persistence?
Risposte:
A volte hai due entità e c'è una relazione tra loro. Ad esempio, potresti avere un'entità chiamata University
e un'altra entità chiamata Student
e 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à:
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:
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.
}
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.
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?
EAGER
il caricamento delle raccolte significa che vengono recuperate completamente al momento del recupero del loro genitore. Quindi, se hai Course
e ha List<Student>
, tutti gli studenti vengono recuperati dal database al momento delCourse
recupero.
LAZY
d'altra parte significa che il contenuto del file List
viene recuperato solo quando si tenta di accedervi. Ad esempio, chiamando course.getStudents().iterator()
. La chiamata a qualsiasi metodo di accesso su List
avvia 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 ArrayList
e HashSet
, ma PersistentSet
e PersistentList
(o PersistentBag
)
course.getStudents()
, genera una query SQL (vista sulla console). Anche nel tipo di recupero pigro, accade la stessa cosa. Quindi, qual è la differenza ??
fetchtype = LAZY
quello predefinito anche se provi a ottenere la raccolta con il getter hibernete genera un errore che mi dice che non può valutare
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.
Per impostazione predefinita, per tutti gli oggetti raccolta e mappa è la regola di recupero FetchType.LAZY
e per altre istanze segue la FetchType.EAGER
politica.
In breve, @OneToMany
e le @ManyToMany
relazioni non recuperano implicitamente gli oggetti correlati (raccolta e mappa), ma l'operazione di recupero viene messa in cascata attraverso il campo in @OneToOne
e @ManyToOne
quelli.
Entrambi FetchType.LAZY
e FetchType.EAGER
vengono 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.
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.
Il Lazy
tipo di recupero è selezionato per impostazione predefinita da Hibernate a meno che non si contrassegni esplicitamente il Eager
tipo 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.
getMember
viene chiamata una funzione chiamata che corrisponde esattamente al modello di nome del membro?
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 @OneToMany
Subject.java o fetchType.LAZY su @ManyToOne
in Books.java.
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; }
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:
FetchType.EAGER
, in modo che la sessione rimanga attiva con il metodo del controller.FetchType.LAZY
con il metodo del convertitore per trasferire i dati da Entity
a un altro oggetto dati DTO
e inviarli al controller, quindi non ci sono eccezioni se la sessione viene chiusa.@ 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;
}
//...
}
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=true
per la propria app e controllare le query emesse dal letargo.