Java Persistence API ve Hibernate'e yeni başladım.
Arasındaki fark nedir FetchType.LAZY
ve FetchType.EAGER
Java Persistence API?
Java Persistence API ve Hibernate'e yeni başladım.
Arasındaki fark nedir FetchType.LAZY
ve FetchType.EAGER
Java Persistence API?
Yanıtlar:
Bazen iki varlığınız olur ve bunlar arasında bir ilişki vardır. Örneğin, şu şekilde adlandırılmış bir varlığınız University
ve başka bir varlığınız Student
olabilir ve bir Üniversitede çok sayıda Öğrenci olabilir:
Üniversite kuruluşu, kimlik, ad, adres vb. Gibi bazı temel özelliklere ve belirli bir üniversite için öğrenci listesini döndüren öğrenciler adlı bir toplama özelliğine sahip olabilir:
public class University {
private String id;
private String name;
private String address;
private List<Student> students;
// setters and getters
}
Artık bir üniversiteyi veritabanından yüklediğinizde, JPA kimlik, ad ve adres alanlarını sizin için yükler. Ancak öğrencilerin nasıl yüklenmesi gerektiğine ilişkin iki seçeneğiniz vardır:
getStudents()
yöntemini çağırdığınızda talep üzerine (yani tembel olarak) yüklemek için .Bir üniversitede çok sayıda öğrenci olduğunda, özellikle ihtiyaç duyulmadığında tüm öğrencilerini birlikte yüklemek verimli değildir ve bu gibi durumlarda öğrencilerin gerçekten ihtiyaç duyulduğunda yüklenmelerini istediğinizi beyan edebilirsiniz. Buna tembel yükleme denir.
İşte students
hevesle yüklenmek üzere açıkça işaretlenmiş bir örnek :
@Entity
public class University {
@Id
private String id;
private String name;
private String address;
@OneToMany(fetch = FetchType.EAGER)
private List<Student> students;
// etc.
}
Ve burada students
tembel olarak yüklenmek üzere açıkça işaretlenmiş bir örnek :
@Entity
public class University {
@Id
private String id;
private String name;
private String address;
@OneToMany(fetch = FetchType.LAZY)
private List<Student> students;
// etc.
}
getStudents()
) Çağırarak belleğe yüklenmek istediğinde JDBC oturumunun hala açık olması gerekir , ancak bazen bu mümkün değildir, çünkü bu yöntem zamana kadar çağrılır, oturum zaten kapatılır ve varlık ayrılır. Benzer şekilde, bazen bir istemci / sunucu mimarimiz (örneğin Swing istemcisi / JEE sunucusu) vardır ve varlıklar / DTO'lar tel üzerinden istemciye aktarılır ve yine bu senaryolarda tembel yükleme varlıkların yolu nedeniyle çalışmaz tel üzerinden serileştirilir.
getStudents()
yöntemi ilk kez çağırdığımda sonuçlar önbelleğe alınmış mı? bir dahaki sefere bu sonuçlara daha hızlı erişebilsem?
EAGER
koleksiyonların yüklenmesi, ebeveynlerinin getirildiği anda tamamen getirildikleri anlamına gelir. Eğer varsa Yani eğer Course
ve sahip olduğu List<Student>
tüm öğrenciler getirilen veritabanından anda Course
getirilir.
LAZY
Öte yandan, içeriğinin List
yalnızca bunlara erişmeye çalıştığınızda getirildiği anlamına gelir . Örneğin, arayarak course.getStudents().iterator()
. Üzerindeki herhangi bir erişim yönteminin çağrılması List
, öğeleri almak için veritabanına bir çağrı başlatır. Bu, List
(veya Set
) etrafında bir Proxy oluşturarak uygulanır . Tembel koleksiyonları için Yani, beton türleri değildir ArrayList
ve HashSet
ancak PersistentSet
ve PersistentList
(veya PersistentBag
)
course.getStudents()
, bir SQL sorgusu başlatır (konsolda gördü). Tembel getirme tipinde de aynı şey olur. Peki, fark nedir ??
fetchtype = LAZY
zaman varsayılan bir setter olsun alıcı olsun hazırda bekletme hibernete bana değerlendiremezsiniz söyleyen bir hata atar bile
Performans ve bellek kullanımını düşünebilirim. Büyük bir fark, EAGER getirme stratejisinin getirilen veri nesnesini oturum olmadan kullanmasına izin vermesidir. Neden?
Oturum bağlandığında nesnede istekli işaretli veriler olduğunda tüm veriler getirilir. Ancak, tembel yükleme stratejisi durumunda, oturumun bağlantısı kesilirse (deyimden sonra session.close()
) tembel yükleme işaretli nesne veri almaz . Tüm bunlar hazırda bekleme proxy'si tarafından yapılabilir. İstekli strateji, oturumu kapattıktan sonra verilerin hala kullanılabilir olmasını sağlar.
Varsayılan olarak, tüm toplama ve harita nesneleri için getirme kuralı FetchType.LAZY
ve diğer örnekler için bu FetchType.EAGER
ilkeyi izler .
Kısacası @OneToMany
ve @ManyToMany
ilişkiler, ilgili nesneleri (toplama ve harita) dolaylı olarak getirmez, ancak geri alma işlemi, alandaki @OneToOne
ve @ManyToOne
alanlardaki alandan basamaklandırılır .
Her ikisi de FetchType.LAZY
ve varsayılan getirme planınıFetchType.EAGER
tanımlamak için kullanılır .
Ne yazık ki, yalnızca LAZY getirme için varsayılan getirme planını geçersiz kılabilirsiniz. EAGER getirme daha az esnektir ve birçok performans sorununa yol açabilir .
Benim tavsiyem derneklerinizi EAGER yapma dürtüsünü kısıtlamaktır çünkü getirme bir sorgu zamanı sorumluluğudur. Bu nedenle, tüm sorgularınız getirme direktifini yalnızca mevcut iş durumu için gerekli olanı almak için kullanmalıdır.
Gönderen Javadoc :
EAGER stratejisi, kalıcılık sağlayıcısı çalışma zamanında verilerin hevesle getirilmesi gereken bir gerekliliktir. LAZY stratejisi, kalıcılık sağlayıcısı çalışma zamanına, verilerin ilk erişildiğinde tembel bir şekilde getirilmesi gerektiğine dair bir ipucudur.
Örneğin, istekli tembel olmaktan daha proaktiftir. Tembel sadece ilk kullanımda olur (sağlayıcı ipucu alırsa), oysa istekli şeylerle (önceden) alınabilir.
Lazy
Getirme türü açıkça işaretlemek sürece hazırda tarafından seçilen varsayılan olarak Eager
türünü getir. Daha doğru ve öz olmak gerekirse, fark aşağıdaki gibi ifade edilebilir.
FetchType.LAZY
= Getter yöntemi ile çağırmadığınız sürece ilişkiler yüklenmez.
FetchType.EAGER
= Bu tüm ilişkileri yükler.
Bu iki getirme türünün Artıları ve Eksileri.
Lazy initialization
gereksiz hesaplamalardan kaçınarak performansı artırır ve bellek gereksinimlerini azaltır.
Eager initialization
daha fazla bellek tüketimi gerektirir ve işlem hızı yavaştır.
Bunu söyledikten sonra, duruma bağlı olarak bu başlatma işlemlerinden herhangi biri kullanılabilir.
getMember
, üyenin adı ile tam olarak eşleşen bir işlev çağrılıncaya kadar özelliği yüklemeyi erteleyecek mi?
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();
}
}
}
Main.java öğesinin retrieve () yöntemini kontrol edin. Subject aldığımızda , ek açıklama içeren koleksiyon listesi@OneToMany
tembel olarak yüklenecektir. Ancak Öte yandan, Kitaplar ilgili açıklama konusu ile ilgili açıklama, açıklama @ManyToOne
eklenir, daha önce yüklenir ( [default][1]
için @ManyToOne
, tarafından fetchType=EAGER
). Subject.java'ya @OneToMany
fetchType.EAGER veya Books.java'da fetchType.LAZY öğesini yerleştirerek davranışı değiştirebiliriz @ManyToOne
.
public enum FetchType, java.lang.Enum veritabanından veri alma stratejilerini tanımlar. EAGER stratejisi, kalıcılık sağlayıcısı çalışma zamanında verilerin hevesle getirilmesi gereken bir gerekliliktir. LAZY stratejisi, kalıcılık sağlayıcısı çalışma zamanına, verilerin ilk erişildiğinde tembel bir şekilde getirilmesi gerektiğine dair bir ipucudur. Uygulamaya, LAZY strateji ipucunun belirtildiği verileri hevesle getirmesine izin verilir. Örnek: @Basic (fetch = LAZY) korumalı Dize getName () {dönüş adı; }
Bu notu "Kyung Hwan Min" in yukarıda söylediklerine eklemek istiyorum.
Bu basit mimarla Spring Rest kullandığınızı varsayalım:
Denetleyici <-> Hizmet <-> Deposu
Ve bazı verileri kullanıcı arabirimine döndürmek istiyorsunuz, kullanıyorsanız FetchType.LAZY
, oturum Hizmette kapatıldığı için verileri denetleyici yöntemine döndürdükten sonra bir istisna JSON Mapper Object
elde edersiniz, böylece veriler elde edilemez.
Bu sorunu çözmek için üç yaygın seçenek vardır, tasarıma, performansa ve geliştiriciye bağlıdır:
FetchType.EAGER
, Böylece oturum hala denetleyici yönteminde hayatta kalır .FetchType.LAZY
verileri Entity
başka bir veri nesnesine aktarmak DTO
ve denetleyiciye göndermek için dönüştürücü yöntemiyle kullanmaktır , bu nedenle oturum kapalıysa bir istisna yoktur.Hazırda Bekletme modunu kullanıyorsanız @ drop-shadow Hibernate.initialize()
, getStudents()
yöntemi çağırdığınızda arayabilirsiniz :
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: Alt varlıkları tembel olarak getirir, yani ana varlığın getirilmesi sırasında, sadece alt varlıkların proxy'sini (cglib veya başka bir yardımcı program tarafından oluşturulan) alır ve alt varlığın herhangi bir özelliğine eriştiğinizde, aslında hazırda bekletme tarafından getirilir.
EAGER: ebeveynleri ile birlikte çocuk varlıklarını getirir.
Daha iyi anlamak için Jboss belgelerine gidin ya da hibernate.show_sql=true
uygulamanız için kullanabilirsiniz ve hazırda bekletme tarafından yayınlanan sorguları kontrol edebilirsiniz.