Java Persistence API'sinde FetchType LAZY ve EAGER arasındaki fark nedir?


552

Java Persistence API ve Hibernate'e yeni başladım.

Arasındaki fark nedir FetchType.LAZYve FetchType.EAGERJava Persistence API?


1
EAGER koleksiyonların yüklenmesi, ebeveynlerinin getirildiği anda tamamen getirildikleri anlamına gelir. EAGER yüklenirken, tüm çocuğum getirilir. Çocuk PersistentSet ve PersistentList'te (veya PersistentBag) getirilir, Persistent Bag içinde bir Array List olarak görüntülenir. Doğru mu ?? ..
geetha

Yanıtlar:


1064

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 Universityve başka bir varlığınız Studentolabilir 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:

Bir üniversitede çok sayıda öğrenci var

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:

  1. Alanların geri kalanıyla (yani hevesle) yüklemek için veya
  2. Üniversitenin 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 studentshevesle 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 studentstembel 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.
}

5
@BehrangSaeedzadeh, bazı pratik farklılıkları veya her bir yükleme türünün avantajlarını ve dezavantajlarını listeleyebilirsiniz (bahsettiğiniz verimlilik dışında). Neden istekli yüklemeyi kullanmak istesin ki?
ADTC

73
@ADTC Tembel yüklemenin çalışması için, hedef varlıklar alıcı yöntemini (örn. 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.
TheFooProgrammer

4
Kitabımdan bu yanıta biraz daha bilgi eklemek istiyorum - Hafızadan tasarruf etmek için, Tembel yükleme genellikle bir ile çok ve çok sayıda ilişki için kullanılır. Bire bir için genellikle Eager kullanılır.
Erran Morad

2
Tembel yüklemede, 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?
JavaTechnical

2
@JavaTechnical ikinci seviye önbelleği etkinleştirip etkinleştirmediğinize bağlıdır (varsayılan olarak etkindir)
Ced

285

Temel olarak,

LAZY = fetch when needed
EAGER = fetch immediately

11
Çok açık ama sadece @ Behang'ın cevabını okuduktan sonra. Net özet için teşekkürler. :-)
Nabin

66

EAGERkoleksiyonların yüklenmesi, ebeveynlerinin getirildiği anda tamamen getirildikleri anlamına gelir. Eğer varsa Yani eğer Courseve sahip olduğu List<Student>tüm öğrenciler getirilen veritabanından anda Coursegetirilir.

LAZYÖte yandan, içeriğinin Listyalnı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 ArrayListve HashSetancak PersistentSetve PersistentList(veya PersistentBag)


Bu kavramı, bir alt varlığın ayrıntılarını getirirken kullandım, ancak aralarında herhangi bir fark göremiyorum. Eager getirmeyi belirlediğimde, her şeyi getirir ve hata ayıkladığımda, alt öğede "Bean ertelendi" ifadesini görüyorum. Söylediğimde course.getStudents(), bir SQL sorgusu başlatır (konsolda gördü). Tembel getirme tipinde de aynı şey olur. Peki, fark nedir ??
Neha Choudhary

istekli koleksiyonlar, sahip olan varlık yüklendiğinde getirilir. Tembel koleksiyonlara eriştiğinizde getirilir. Gördüğünüz davranış bu değilse, muhtemelen çevrenizle ilgili bir sorun vardı (örneğin, bir sınıfın eski sürümlerini çalıştırmak)
Bozho

1
@Bozho Yalnızca koleksiyonların tembel yüklenmesini belirttiniz. Basit bir dize alanı tembel olarak yüklenebilir mi?
vikiiii

Hayır. Sütunların bir alt kümesini almak için bir sorgu veya farklı bir eşlenmiş varlık kullanmanız gerekir
Bozho

@Bozho, hey lütfen cevap verebilir miyim o fetchtype = LAZYzaman varsayılan bir setter olsun alıcı olsun hazırda bekletme hibernete bana değerlendiremezsiniz söyleyen bir hata atar bile
Все Едно

16

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.


11

Bildiğim kadarıyla her iki getirme türü sizin ihtiyacınıza bağlıdır.

FetchType.LAZY talep üzerine (yani verileri istediğimizde).

FetchType.EAGER hemen (yani gereksinimimiz gelmeden önce gereksiz yere kayıt getiriyoruz)


11

Varsayılan olarak, tüm toplama ve harita nesneleri için getirme kuralı FetchType.LAZYve diğer örnekler için bu FetchType.EAGERilkeyi izler .
Kısacası @OneToManyve @ManyToManyilişkiler, ilgili nesneleri (toplama ve harita) dolaylı olarak getirmez, ancak geri alma işlemi, alandaki @OneToOneve @ManyToOnealanlardaki alandan basamaklandırılır .

(nezaket: - objectdbcom)


9

Her ikisi de FetchType.LAZYve 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.


2
"EAGER getirme daha az esnektir ve birçok performans sorununa yol açabilir." ... Daha doğru bir ifade "EAGER getirmeyi kullanmak veya kullanmamak performans sorunlarına yol açabilir" dir. Tembel olarak başlatılan bir alanın erişiminin pahalı olduğu VE nadiren kullanıldığı durumlarda, tembel getirme performansa fayda sağlayacaktır. Ancak, bir değişkenin sıklıkla kullanıldığı durumda, tembel başlatma , veritabanına istekli başlatmadan daha fazla gezi gerektirerek performansı düşürebilir . FetchType'ı dogmatik olarak değil, doğru bir şekilde uygulamanızı öneririm.
scottb

Kitabınızı burada tanıtıyor musunuz? Ama evet bunun kullanım durumuna ve nesnenin büyüklüğünün kardinalite ilişkisinde olduğuna bağlı olduğunu hissediyorum.
John Doe

6

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.


1
"ilk kullanım" ile ne demek istiyorsun?
leon

@leon: İstekli bir alana ve tembel bir alana sahip bir varlığınız olduğunu varsayalım. Varlığı aldığınızda, istekli alan varlık referansını aldığınızda DB'den yüklenir, ancak tembel alan bulunmamış olabilir. Yalnızca alana erişimcisi aracılığıyla erişmeye çalıştığınızda getirilir .
TJ Crowder

@TJ Crowder, hiçbir fetchtype tanımlanmadığında varsayılan değer nedir?
Mahmoud Saleh

@MahmoudSaleh: Hiçbir fikrim yok. Muhtemelen bir şeye göre değişir. JPA'yı gerçek bir projede kullanmadım, bu yüzden cesaretine girmedim.
TJ Crowder

2
@MahmoudS: Varsayılan getirmeler: OneToMany: LAZY, ManyToOne: EAGER, ManyToMany: LAZY, OneToOne: EAGER, Sütunlar: EAGER
Markus Pscheidt

5

LazyGetirme türü açıkça işaretlemek sürece hazırda tarafından seçilen varsayılan olarak Eagertü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.


1
"Getter yöntemiyle çağırmadıkça ilişkileri yüklemediği" ifadesinin not edilmesi önemlidir ve aynı zamanda benim görüşümde oldukça geciktirilmiş bir tasarım kararı… Sadece erişim üzerine getireceğini düşündüğüm bir durumla karşılaştım ve öyle olmadı, çünkü bunun için bir alıcı işlevini açıkça çağırmadım. Bu arada, "alıcı" işlevini ne oluşturur? JPA getMember, üyenin adı ile tam olarak eşleşen bir işlev çağrılıncaya kadar özelliği yüklemeyi erteleyecek mi?
ToVine

3

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 @ManyToOneeklenir, daha önce yüklenir ( [default][1]için @ManyToOne, tarafından fetchType=EAGER). Subject.java'ya @OneToManyfetchType.EAGER veya Books.java'da fetchType.LAZY öğesini yerleştirerek davranışı değiştirebiliriz @ManyToOne.


1

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ı; }

Kaynak


1

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 Objectelde 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:

  1. En kolayı kullanmaktır FetchType.EAGER, Böylece oturum hala denetleyici yönteminde hayatta kalır .
  2. Anti-desen çözümleri, yürütme bitene kadar oturumu canlı hale getirmek için, sistemde büyük bir performans sorunu yaratıyor.
  3. En iyi uygulama, FetchType.LAZYverileri Entitybaşka bir veri nesnesine aktarmak DTOve denetleyiciye göndermek için dönüştürücü yöntemiyle kullanmaktır , bu nedenle oturum kapalıysa bir istisna yoktur.

1

Merhaba, bunu anlamanıza yardımcı olmak için 2 fotoğraf ekledim. resim açıklamasını buraya girin

resim açıklamasını buraya girin


0

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;
    }
    //...
}

0

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=trueuygulamanız için kullanabilirsiniz ve hazırda bekletme tarafından yayınlanan sorguları kontrol edebilirsiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.