Ich bin ein Neuling in Java Persistence API und Hibernate.
Was ist der Unterschied zwischen FetchType.LAZY
und FetchType.EAGER
in der Java Persistence API?
Ich bin ein Neuling in Java Persistence API und Hibernate.
Was ist der Unterschied zwischen FetchType.LAZY
und FetchType.EAGER
in der Java Persistence API?
Antworten:
Manchmal haben Sie zwei Entitäten und es gibt eine Beziehung zwischen ihnen. Beispielsweise könnte eine Entität aufgerufen University
und eine andere Entität aufgerufen werden, Student
und eine Universität könnte viele Studenten haben:
Die Universitätsentität verfügt möglicherweise über einige grundlegende Eigenschaften wie ID, Name, Adresse usw. sowie über eine Sammlungseigenschaft namens "Studenten", die die Liste der Studenten für eine bestimmte Universität zurückgibt:
public class University {
private String id;
private String name;
private String address;
private List<Student> students;
// setters and getters
}
Wenn Sie jetzt eine Universität aus der Datenbank laden, lädt JPA die Felder für ID, Name und Adresse für Sie. Sie haben jedoch zwei Möglichkeiten, wie Schüler geladen werden sollen:
getStudents()
Methode der Universität aufrufen .Wenn eine Universität viele Studenten hat, ist es nicht effizient, alle ihre Studenten zusammen mit ihnen zu laden, insbesondere wenn sie nicht benötigt werden. In solchen Fällen können Sie erklären, dass Studenten geladen werden sollen, wenn sie tatsächlich gebraucht werden. Dies wird als verzögertes Laden bezeichnet.
Hier ist ein Beispiel, in dem students
explizit markiert ist, dass es eifrig geladen werden soll:
@Entity
public class University {
@Id
private String id;
private String name;
private String address;
@OneToMany(fetch = FetchType.EAGER)
private List<Student> students;
// etc.
}
Und hier ist ein Beispiel, in dem students
explizit markiert ist, dass es träge geladen werden soll:
@Entity
public class University {
@Id
private String id;
private String name;
private String address;
@OneToMany(fetch = FetchType.LAZY)
private List<Student> students;
// etc.
}
getStudents()
) in den Speicher geladen werden sollen. Dies ist jedoch manchmal nicht möglich, da diese Methode zu diesem Zeitpunkt nicht verfügbar ist aufgerufen wird, ist die Sitzung bereits geschlossen und die Entität getrennt. In ähnlicher Weise haben wir manchmal eine Client / Server-Architektur (z. B. Swing-Client / JEE-Server) und die Entitäten / DTOs werden über die Leitung an den Client übertragen, und in diesen Szenarien funktioniert das verzögerte Laden aufgrund der Art und Weise der Entitäten meistens nicht werden über das Kabel serialisiert.
getStudents()
die Ergebnisse zwischengespeichert , wenn ich die Methode zum ersten Mal aufrufe? damit ich beim nächsten Mal schneller auf diese Ergebnisse zugreifen kann?
EAGER
Das Laden von Sammlungen bedeutet, dass sie zum Zeitpunkt des Abrufs ihres übergeordneten Elements vollständig abgerufen werden. Wenn Sie also haben Course
und haben List<Student>
, werden alle Schüler zum Zeitpunkt des Abrufs aus der DatenbankCourse
abgerufen.
LAZY
Auf der anderen Seite bedeutet dies, dass der Inhalt von List
nur abgerufen wird, wenn Sie versuchen, darauf zuzugreifen. Zum Beispiel durch einen Anruf course.getStudents().iterator()
. Durch Aufrufen einer beliebigen Zugriffsmethode auf List
wird ein Aufruf der Datenbank zum Abrufen der Elemente ausgelöst. Dies wird implementiert, indem ein Proxy um das List
(oder Set
) erstellt wird. Für Ihre faulen Sammlungen sind die konkreten Typen also nicht ArrayList
und HashSet
, sondern PersistentSet
und PersistentList
(oder PersistentBag
)
course.getStudents()
, wird eine SQL-Abfrage ausgelöst (siehe auf der Konsole). Auch beim Lazy-Fetch-Typ passiert dasselbe. Also, was ist der Unterschied?
fetchtype = LAZY
die Standardeinstellung gesetzt ist, auch wenn der Versuch, die Sammlung mit dem Getter Hibernete zu erhalten, einen Fehler auslöst, der mir sagt, dass es nicht ausgewertet werden kann
Ich kann Leistung und Speichernutzung berücksichtigen. Ein großer Unterschied besteht darin, dass die EAGER-Abrufstrategie die Verwendung von abgerufenen Datenobjekten ohne Sitzung ermöglicht. Warum?
Alle Daten werden abgerufen, wenn eifrig markierte Daten im Objekt vorhanden sind, wenn die Sitzung verbunden ist. Im Falle einer Strategie zum verzögerten Laden ruft das markierte Objekt zum verzögerten Laden jedoch keine Daten ab, wenn die Sitzung getrennt wird (nach der session.close()
Anweisung). All dies kann durch einen Ruhezustand-Proxy erfolgen. Mit einer eifrigen Strategie können Daten nach Abschluss der Sitzung weiterhin verfügbar sein.
Standardmäßig gilt für alle Sammlungs- und Zuordnungsobjekte die Abrufregel FetchType.LAZY
und für andere Fälle folgt sie der FetchType.EAGER
Richtlinie.
Kurz gesagt , @OneToMany
und @ManyToMany
Beziehungen rufen die zugehörigen Objekte (Sammlung und Karte) nicht implizit ab, sondern die Abrufoperation wird durch das Feld in @OneToOne
und @ManyToOne
diejenigen kaskadiert .
Beide FetchType.LAZY
und FetchType.EAGER
werden verwendet, um den Standardabrufplan zu definieren .
Leider können Sie nur den Standardabrufplan für das LAZY-Abrufen überschreiben. Das Abrufen von EAGER ist weniger flexibel und kann zu vielen Leistungsproblemen führen .
Mein Rat ist, den Drang zu unterdrücken, Ihre Assoziationen zu EAGER zu machen, da das Abrufen in der Verantwortung der Abfragezeit liegt. Daher sollten alle Ihre Abfragen die Abrufanweisung verwenden, um nur das abzurufen, was für den aktuellen Geschäftsfall erforderlich ist.
Aus dem Javadoc :
Die EAGER-Strategie ist eine Anforderung an die Laufzeit des Persistenzanbieters, dass Daten eifrig abgerufen werden müssen. Die LAZY-Strategie ist ein Hinweis auf die Laufzeit des Persistenzanbieters, dass Daten beim ersten Zugriff träge abgerufen werden sollten.
ZB ist eifriger proaktiv als faul. Faulheit tritt nur beim ersten Gebrauch auf (wenn der Anbieter den Hinweis versteht), wohingegen bei eifrigen Dingen (möglicherweise) Dinge vorab abgerufen werden.
Der Lazy
Abruftyp wird standardmäßig im Ruhezustand ausgewählt, es sei denn, Sie markieren den Eager
Abruftyp explizit . Um genauer und prägnanter zu sein, kann der Unterschied wie folgt angegeben werden.
FetchType.LAZY
= Dadurch werden die Beziehungen nur geladen, wenn Sie sie über die Getter-Methode aufrufen.
FetchType.EAGER
= Dies lädt alle Beziehungen.
Vor- und Nachteile dieser beiden Abruftypen.
Lazy initialization
Verbessert die Leistung, indem unnötige Berechnungen vermieden und der Speicherbedarf reduziert werden.
Eager initialization
nimmt mehr Speicherplatz in Anspruch und die Verarbeitungsgeschwindigkeit ist langsam.
Having said that, hängt von der Situation entweder eines dieser Initialisierung verwendet werden.
getMember
wird, die genau dem Namensmuster des Mitglieds entspricht?
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();
}
}
}
Überprüfen Sie die Methode retrieve () von Main.java. Wenn wir den Betreff erhalten , werden die mit Anmerkungen versehenen Sammlungslistenbücher träge@OneToMany
geladen. Aber auf der anderen Seite, Bücher im Zusammenhang Vereinigung der Sammlung Themas , mit Anmerkungen versehen mit @ManyToOne
, Lasten eargerly (von [default][1]
für @ManyToOne
, fetchType=EAGER
). Wir können das Verhalten ändern, indem wir fetchType.EAGER auf @OneToMany
Subject.java oder fetchType.LAZY auf @ManyToOne
Books.java setzen.
public enum FetchType erweitert java.lang.Enum Definiert Strategien zum Abrufen von Daten aus der Datenbank. Die EAGER-Strategie ist eine Anforderung an die Laufzeit des Persistenzanbieters, dass Daten eifrig abgerufen werden müssen. Die LAZY-Strategie ist ein Hinweis auf die Laufzeit des Persistenzanbieters, dass Daten beim ersten Zugriff träge abgerufen werden sollten. Die Implementierung darf eifrig Daten abrufen, für die der LAZY-Strategiehinweis angegeben wurde. Beispiel: @Basic (fetch = LAZY) protected String getName () {return name; }}
Ich möchte diese Notiz zu dem hinzufügen, was "Kyung Hwan Min" oben gesagt hat.
Angenommen, Sie verwenden Spring Rest mit diesem einfachen Architekten:
Controller <-> Service <-> Repository
Wenn Sie einige Daten an das Front-End zurückgeben möchten, erhalten Sie bei Verwendung FetchType.LAZY
eine Ausnahme, nachdem Sie Daten an die Controller-Methode zurückgegeben haben, da die Sitzung im Service geschlossen JSON Mapper Object
ist und die Daten nicht abgerufen werden können.
Es gibt drei gängige Optionen zur Lösung dieses Problems, die vom Design, der Leistung und dem Entwickler abhängen:
FetchType.EAGER
, damit die Sitzung bei der Controller-Methode noch aktiv ist.FetchType.LAZY
mit der Konvertermethode Daten von Entity
einem anderen Datenobjekt zu übertragen DTO
und an den Controller zu senden. Es gibt also keine Ausnahme, wenn die Sitzung geschlossen wird.@ drop-shadow Wenn Sie den Ruhezustand verwenden, können Hibernate.initialize()
Sie beim Aufrufen der getStudents()
Methode Folgendes aufrufen :
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: Es ruft die untergeordneten Entitäten träge ab, dh zum Zeitpunkt des Abrufs der übergeordneten Entität ruft es nur den Proxy (erstellt von cglib oder einem anderen Dienstprogramm) der untergeordneten Entitäten ab, und wenn Sie auf eine Eigenschaft der untergeordneten Entität zugreifen, wird es tatsächlich im Ruhezustand abgerufen.
EAGER: Es ruft die untergeordneten Entitäten zusammen mit den übergeordneten Entitäten ab.
Zum besseren Verständnis gehen Sie zur Jboss-Dokumentation oder verwenden Sie sie hibernate.show_sql=true
für Ihre App und überprüfen Sie die vom Ruhezustand ausgegebenen Abfragen.