Jestem nowicjuszem Java Persistence API i Hibernacji.
Jaka jest różnica między FetchType.LAZY
i FetchType.EAGER
w interfejsie API Java Persistence?
Jestem nowicjuszem Java Persistence API i Hibernacji.
Jaka jest różnica między FetchType.LAZY
i FetchType.EAGER
w interfejsie API Java Persistence?
Odpowiedzi:
Czasami masz dwa byty i istnieje między nimi związek. Na przykład możesz mieć jednostkę o nazwie, University
a inna jednostka o nazwie, Student
a uniwersytet może mieć wielu studentów:
Jednostka uniwersytecka może mieć pewne podstawowe właściwości, takie jak identyfikator, nazwa, adres itp., A także właściwość kolekcji zwaną studentami, która zwraca listę studentów dla danej uczelni:
public class University {
private String id;
private String name;
private String address;
private List<Student> students;
// setters and getters
}
Teraz, gdy ładujesz uniwersytet z bazy danych, JPA ładuje dla ciebie pola identyfikatora, nazwy i adresu. Ale masz dwie opcje dotyczące sposobu ładowania uczniów:
getStudents()
metodę uniwersytecką .Gdy uniwersytet ma wielu studentów, nie jest efektywnie ładować wszystkich studentów razem z nim, szczególnie gdy nie są oni potrzebni, aw takich przypadkach możesz zadeklarować, że chcesz, aby studenci zostali załadowani, gdy są faktycznie potrzebni. Nazywa się to leniwym ładowaniem.
Oto przykład, w którym students
jest wyraźnie zaznaczony do szybkiego załadowania:
@Entity
public class University {
@Id
private String id;
private String name;
private String address;
@OneToMany(fetch = FetchType.EAGER)
private List<Student> students;
// etc.
}
A oto przykład, w którym students
jest wyraźnie zaznaczony do załadowania leniwie:
@Entity
public class University {
@Id
private String id;
private String name;
private String address;
@OneToMany(fetch = FetchType.LAZY)
private List<Student> students;
// etc.
}
getStudents()
), Ale czasami nie jest to możliwe, ponieważ do czasu tej metody jest wywoływana, sesja jest już zamknięta, a jednostka odłączona. Podobnie, czasami mamy architekturę klient / serwer (np. Klient Swing / serwer JEE), a podmioty / DTO są przesyłane przewodowo do klienta i ponownie najczęściej w tych scenariuszach leniwe ładowanie nie będzie działać ze względu na sposób, w jaki podmioty są serializowane za pomocą drutu.
getStudents()
metodę po raz pierwszy, wyniki są buforowane? aby następnym razem uzyskać szybszy dostęp do tych wyników?
EAGER
ładowanie kolekcji oznacza, że są one pobierane w całości w momencie pobierania ich rodzica. Więc jeśli masz Course
i tak List<Student>
, wszyscy studenci są pobierani z bazy danych w momencie Course
pobierania.
LAZY
z drugiej strony oznacza, że zawartość List
jest pobierana tylko wtedy, gdy próbujesz uzyskać do nich dostęp. Na przykład dzwoniąc course.getStudents().iterator()
. Wywołanie dowolnej metody dostępu List
zainicjuje wywołanie bazy danych w celu pobrania elementów. Jest to realizowane przez utworzenie proxy wokół List
(lub Set
). Tak więc dla twoich leniwych kolekcji konkretne typy nie są ArrayList
i HashSet
, ale PersistentSet
i PersistentList
(lub PersistentBag
)
course.getStudents()
, uruchamia zapytanie SQL (widziałem to na konsoli). W typie pobierania Lazy również dzieje się to samo. Więc jaka jest różnica?
fetchtype = LAZY
domyślny, nawet jeśli spróbujesz pobrać kolekcję za pomocą gettera hibernety zgłasza błąd informujący, że nie może ocenić
Mogę rozważyć wydajność i wykorzystanie pamięci. Jedną dużą różnicą jest to, że strategia pobierania EAGER pozwala na użycie pobranego obiektu danych bez sesji. Dlaczego?
Wszystkie dane są pobierane, gdy chętnie zaznaczone dane w obiekcie, gdy sesja jest połączona. Jednak w przypadku leniwej strategii ładowania, leniwe ładowanie zaznaczonego obiektu nie pobiera danych, jeśli sesja zostanie rozłączona (po session.close()
instrukcji). Wszystko, co można zrobić przez hibernację proxy. Szybka strategia pozwala, aby dane były nadal dostępne po sesji zamknięcia.
Domyślnie dla wszystkich obiektów kolekcji i map obowiązuje reguła pobierania, FetchType.LAZY
aw innych przypadkach jest zgodna z FetchType.EAGER
zasadami.
W skrócie, @OneToMany
a @ManyToMany
stosunki nie sprowadzić powiązanych obiektów (zbieranie i map) implicictly ale odzyskiwanie operacja jest kaskadowo przez pola w @OneToOne
i @ManyToOne
te.
Oba FetchType.LAZY
i FetchType.EAGER
służą do zdefiniowania domyślnego planu pobierania .
Niestety możesz zastąpić tylko domyślny plan pobierania dla LAZY pobierania. Pobieranie EAGER jest mniej elastyczne i może prowadzić do wielu problemów z wydajnością .
Radzę powstrzymać potrzebę tworzenia stowarzyszeń EAGER, ponieważ pobieranie jest obowiązkiem czasu zapytania. Dlatego wszystkie zapytania powinny korzystać z dyrektywy pobierania, aby pobrać tylko to, co jest konieczne w bieżącym uzasadnieniu biznesowym.
Z Javadoc :
Strategia EAGER jest wymogiem w środowisku wykonawczym dostawcy trwałości, że dane muszą być chętnie pobierane. Strategia LAZY jest wskazówką dla środowiska wykonawczego dostawcy trwałości, że dane powinny być pobierane leniwie przy pierwszym dostępie.
Np. Chętny jest bardziej proaktywny niż leniwy. Leniwy zdarza się tylko przy pierwszym użyciu (jeśli dostawca skorzysta z podpowiedzi), podczas gdy z niecierpliwymi rzeczami (może) zostać wstępnie pobrany.
Typ Lazy
pobierania jest domyślnie wybierany przez Hibernacja, chyba że wyraźnie zaznaczysz Eager
typ pobierania. Aby być bardziej dokładnym i zwięzłym, różnicę można określić jak poniżej.
FetchType.LAZY
= To nie ładuje relacji, chyba że wywołasz je metodą gettera.
FetchType.EAGER
= To ładuje wszystkie relacje.
Plusy i minusy tych dwóch typów pobierania.
Lazy initialization
poprawia wydajność, unikając niepotrzebnych obliczeń i zmniejszając wymagania dotyczące pamięci.
Eager initialization
zajmuje więcej pamięci, a szybkość przetwarzania jest niska.
To powiedziawszy, zależy od sytuacji, w której można zastosować jedną z tych inicjalizacji.
getMember
będzie dokładnie pasować do wzorca nazwy członka?
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();
}
}
}
Sprawdź metodę retrieve () w Main.java. Kiedy otrzymamy Temat, jego lista kolekcji Książki , opatrzone adnotacjami @OneToMany
, zostaną załadowane leniwie. Ale z drugiej strony, Książki podobne stowarzyszenie odbioru przedmiotu , z uwagami @ManyToOne
, ładunki eargerly (autorem jest [default][1]
za @ManyToOne
, fetchType=EAGER
). Możemy zmienić zachowanie, umieszczając fetchType.EAGER na @OneToMany
Subject.java lub fetchType.LAZY na @ManyToOne
Books.java.
publiczne wyliczanie FetchType rozszerza java.lang.Enum Definiuje strategie pobierania danych z bazy danych. Strategia EAGER jest wymogiem w środowisku wykonawczym dostawcy trwałości, że dane muszą być chętnie pobierane. Strategia LAZY jest wskazówką dla środowiska wykonawczego dostawcy trwałości, że dane powinny być pobierane leniwie przy pierwszym dostępie. Implementacja może chętnie pobierać dane, dla których określono wskazówkę strategiczną LAZY. Przykład: @Basic (fetch = LAZY) chroniony ciąg getName () {return name; }
Chcę dodać tę notatkę do tego, co powiedział „Kyung Hwan Min” powyżej.
Załóżmy, że używasz Spring Rest z tym prostym architektem:
Kontroler <-> Usługa <-> Repozytorium
I chcesz zwrócić niektóre dane do interfejsu, jeśli używasz FetchType.LAZY
, otrzymasz wyjątek po zwróceniu danych do metody kontrolera, ponieważ sesja jest zamknięta w usłudze, więc JSON Mapper Object
nie można uzyskać danych.
Istnieją trzy typowe opcje rozwiązania tego problemu, w zależności od projektu, wydajności i dewelopera:
FetchType.EAGER
, aby sesja nadal żyła metodą kontrolera.FetchType.LAZY
metody konwertera do przesyłania danych z Entity
innego obiektu danych DTO
i wysyłania go do kontrolera, więc nie ma wyjątku, jeśli sesja zostanie zamknięta.@ drop-shadow, jeśli używasz Hibernacji, możesz wywoływać Hibernate.initialize()
po wywołaniu getStudents()
metody:
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: Leniwie pobiera encje potomne, tzn. W momencie pobierania encji nadrzędnej pobiera tylko proxy (utworzone przez cglib lub dowolne inne narzędzie) encji potomnych, a kiedy uzyskujesz dostęp do dowolnej właściwości encji podrzędnej, jest ona faktycznie pobierana przez hibernację.
EAGER: pobiera byty potomne wraz z rodzicem.
Aby lepiej zrozumieć, przejdź do dokumentacji Jboss lub możesz użyć hibernate.show_sql=true
aplikacji i sprawdzić zapytania wygenerowane przez hibernację.