Perbedaan antara FetchType LAZY dan EAGER di Java Persistence API?


552

Saya seorang pemula untuk Java Persistence API dan Hibernate.

Apa perbedaan antara FetchType.LAZYdan FetchType.EAGERdi Java Persistence API?


1
Pemuatan koleksi EAGER berarti bahwa koleksi tersebut diambil sepenuhnya pada saat orang tua mereka diambil. Saat EAGER sedang memuat, maka semua anak saya diambil. Anak itu diambil di PersistentSet dan PersistentList (atau PersistentBag), di dalam Persistent Bag, itu ditampilkan sebagai Daftar Array. Benarkah ?? ..
geetha

Jawaban:


1064

Terkadang Anda memiliki dua entitas dan ada hubungan di antara mereka. Misalnya, Anda mungkin memiliki entitas yang dipanggil Universitydan entitas lain yang dipanggil Studentdan Universitas mungkin memiliki banyak Mahasiswa:

Entitas Universitas mungkin memiliki beberapa properti dasar seperti id, nama, alamat, dll. Serta properti koleksi yang disebut siswa yang mengembalikan daftar siswa untuk universitas tertentu:

Universitas memiliki banyak siswa

public class University {
   private String id;
   private String name;
   private String address;
   private List<Student> students;

   // setters and getters
}

Sekarang ketika Anda memuat Universitas dari database, JPA memuat bidang id, nama, dan alamatnya untuk Anda. Tetapi Anda memiliki dua opsi untuk bagaimana siswa harus dimuat:

  1. Untuk memuatnya bersama dengan bidang lainnya (yaitu dengan penuh semangat), atau
  2. Untuk memuatnya sesuai permintaan (yaitu malas) saat Anda memanggil metode universitas getStudents().

Ketika sebuah universitas memiliki banyak siswa, tidaklah efisien untuk memuat semua mahasiswanya bersama-sama dengannya, terutama ketika mereka tidak dibutuhkan dan dalam kasus-kasus serupa Anda dapat menyatakan bahwa Anda ingin siswa dimuat ketika mereka sebenarnya dibutuhkan. Ini disebut pemuatan malas.

Berikut ini contoh, di mana studentsditandai secara eksplisit untuk dimuat dengan penuh semangat:

@Entity
public class University {

    @Id
    private String id;

    private String name;

    private String address;

    @OneToMany(fetch = FetchType.EAGER)
    private List<Student> students;

    // etc.    
}

Dan inilah contoh di mana studentssecara eksplisit ditandai untuk dimuat dengan malas:

@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 dapatkah Anda membuat daftar beberapa perbedaan praktis, atau kelebihan dan kekurangan dari setiap jenis pemuatan (selain efisiensi yang Anda sebutkan). Mengapa orang ingin menggunakan eager loading?
ADTC

73
@ADTC Agar lazy loading berfungsi, sesi JDBC masih harus terbuka ketika entitas target ingin dimuat ke dalam memori dengan memanggil metode pengambil (mis. getStudents()), Tetapi kadang-kadang ini tidak mungkin, karena pada saat metode ini dipanggil, sesi sudah ditutup dan entitas terlepas. Demikian pula, kadang-kadang kita memiliki arsitektur klien / server (misalnya klien Swing / server JEE) dan entitas / DTO ditransfer melalui kawat ke klien dan sekali lagi paling sering dalam skenario ini pemuatan malas tidak akan berfungsi karena cara entitas diserialkan melalui kabel.
TheFooProgrammer

4
Saya ingin menambahkan beberapa info ke jawaban ini dari buku saya - Untuk menghemat memori, pemuatan Malas umumnya digunakan untuk satu ke banyak dan banyak ke banyak hubungan. Untuk satu lawan satu, umumnya Eager digunakan.
Erran Morad

2
Dalam pemuatan malas, ketika saya memanggil getStudents()metode untuk pertama kalinya, apakah hasilnya di-cache? sehingga saya dapat mengakses hasil tersebut lebih cepat di waktu berikutnya?
JavaTechnical

2
@JavaTechnical tergantung jika Anda mengaktifkan cache level kedua (diaktifkan secara default)
Ced

285

Pada dasarnya,

LAZY = fetch when needed
EAGER = fetch immediately

11
Sangat jelas tetapi hanya setelah membaca jawaban @ Behang. Terima kasih atas ringkasan yang jelas. :-)
Nabin

66

EAGERmemuat koleksi berarti bahwa mereka diambil sepenuhnya pada saat orang tua mereka diambil. Jadi jika sudah Coursedan sudah List<Student>, semua siswa diambil dari database pada saat Coursediambil.

LAZYdi sisi lain berarti bahwa konten Listdiambil hanya ketika Anda mencoba mengaksesnya. Misalnya dengan menelepon course.getStudents().iterator(). Memanggil metode akses apa pun pada Listakan memulai panggilan ke database untuk mengambil elemen. Ini diimplementasikan dengan membuat Proxy di sekitar List(atau Set). Jadi untuk koleksi malas Anda, jenis betonnya bukan ArrayListdan HashSet, tapi PersistentSetdan PersistentList(atau PersistentBag)


Saya menggunakan konsep itu dalam mengambil detail entitas anak, tetapi saya tidak bisa melihat perbedaan di antara mereka. Ketika saya menentukan Eager mengambil, Itu mengambil semuanya dan Ketika saya men-debug itu, saya melihat "Bean ditangguhkan" di entitas anak. Ketika saya katakan course.getStudents(), ia meluncurkan query SQL (melihatnya di konsol). Dalam jenis pengambilan Malas juga, hal yang sama terjadi. Jadi, apa bedanya ??
Neha Choudhary

koleksi yang cepat diambil ketika entitas yang dimiliki dimuat. Koleksi malas diambil saat Anda mengaksesnya. Jika ini bukan perilaku gergaji Anda, mungkin ada sesuatu yang salah dengan lingkungan Anda (mis, menjalankan versi kelas yang lama)
Bozho

1
@Bozho Anda menetapkan pemuatan malas koleksi saja. Bisakah bidang string sederhana dimuat malas?
vikiiii

Tidak. Anda perlu menggunakan kueri atau entitas yang dipetakan berbeda untuk mendapatkan subset kolom
Bozho

@Bozho, hei bisakah Anda menjawab ini kemudian jika sudah ditetapkan pada fetchtype = LAZYdefault bahkan jika mencoba untuk mendapatkan koleksi dengan pengambil hibernete melempar kesalahan mengatakan saya itu tidak dapat mengevaluasi
Все Едно

16

Saya dapat mempertimbangkan kinerja dan pemanfaatan memori. Satu perbedaan besar adalah bahwa strategi pengambilan EAGER memungkinkan untuk menggunakan objek data yang diambil tanpa sesi. Mengapa?
Semua data diambil saat ingin menandai data di objek saat sesi terhubung. Namun, dalam kasus strategi pemuatan malas, pemuatan malas objek yang ditandai tidak mengambil data jika sesi terputus (setelah session.close()pernyataan). Semua itu bisa dilakukan dengan hibernate proxy. Strategi yang bersemangat memungkinkan data tetap tersedia setelah sesi penutupan.


11

Sesuai pengetahuan saya, kedua jenis pengambilan tergantung kebutuhan Anda.

FetchType.LAZY sesuai permintaan (yaitu saat kami membutuhkan data).

FetchType.EAGER langsung (yaitu sebelum persyaratan kami datang kami tidak perlu mengambil catatan)


11

Secara default, untuk semua objek koleksi dan peta, aturan pengambilannya adalah FetchType.LAZYdan untuk kasus lain, ia mengikuti FetchType.EAGERkebijakan.
Singkatnya, @OneToManydan @ManyToManyrelasi tidak mengambil objek terkait (pengumpulan dan peta) secara implisit tetapi operasi pengambilan mengalir melalui bidang @OneToOnedan objek @ManyToOne.

(courtesy: - objectdbcom)


9

Keduanya FetchType.LAZYdan FetchType.EAGERdigunakan untuk menentukan rencana pengambilan default .

Sayangnya, Anda hanya dapat mengganti rencana pengambilan default untuk pengambilan LAZY. Pengambilan EAGER kurang fleksibel dan dapat menyebabkan banyak masalah kinerja .

Saran saya adalah untuk menahan keinginan membuat asosiasi Anda LEBIH BAIK karena mengambil adalah tanggung jawab waktu permintaan. Jadi semua pertanyaan Anda harus menggunakan arahan ambil untuk hanya mengambil apa yang diperlukan untuk kasus bisnis saat ini.


2
"Pengambilan EAGER kurang fleksibel dan dapat menyebabkan banyak masalah kinerja." ... Pernyataan yang lebih benar adalah "Menggunakan atau tidak menggunakan pengambilan EAGER dapat menyebabkan masalah kinerja". Dalam kasus tertentu ketika bidang yang diinisialisasi malas mahal untuk diakses DAN jarang digunakan, pengambilan malas akan menguntungkan kinerja. Tetapi, dalam kasus ketika variabel sering digunakan, inisialisasi malas sebenarnya dapat menurunkan kinerja dengan membutuhkan lebih banyak perjalanan ke database daripada inisialisasi bersemangat. Saya sarankan menerapkan FetchType dengan benar, bukan dogmatis.
scottb

Apakah Anda mempromosikan buku Anda di sini !!. Tapi ya saya merasa itu tergantung pada use case, dan ukuran objek yang dimaksud dalam hubungan kardinalitas.
John Doe

6

Dari Javadoc :

Strategi EAGER adalah persyaratan pada runtime penyedia persistensi bahwa data harus diambil dengan penuh semangat. Strategi LAZY adalah petunjuk untuk runtime penyedia persistensi bahwa data harus diambil dengan malas ketika pertama kali diakses.

Misalnya, bersemangat lebih proaktif daripada malas. Malas hanya terjadi pada penggunaan pertama (jika penyedia mengambil petunjuk), sedangkan dengan hal-hal yang ingin (mungkin) mendapatkan pre-fetched.


1
apa yang Anda maksud dengan "penggunaan pertama"?
leon

@leon: Katakanlah Anda memiliki entitas dengan bidang bersemangat dan bidang malas. Ketika Anda mendapatkan entitas, bidang keinginan akan telah dimuat dari DB pada saat Anda menerima referensi entitas, tetapi bidang malas mungkin belum. Itu akan diambil hanya ketika Anda mencoba mengakses bidang melalui pengaksesnya .
TJ Crowder

@TJ Crowder, apa standarnya ketika tidak ada fetchtype yang ditentukan?
Mahmoud Saleh

@ MahmoudSaleh: Saya tidak tahu. Mungkin bervariasi berdasarkan sesuatu. Saya belum menggunakan JPA dalam proyek nyata jadi saya belum masuk ke dalamnya.
TJ Crowder

2
@MahmoudS: Fetchtypes default: OneToMany: LAZY, ManyToOne: EAGER, ManyToMany: LAZY, OneToOne: EAGER, Kolom: EAGER
Markus Pscheidt

5

The LazyFetch jenis adalah secara default dipilih oleh Hibernate kecuali Anda secara eksplisit menandai EagerAmbil jenis. Agar lebih akurat dan ringkas, perbedaan dapat dinyatakan sebagai berikut.

FetchType.LAZY = Ini tidak memuat hubungan kecuali Anda memintanya melalui metode pengambil.

FetchType.EAGER = Ini memuat semua hubungan.

Pro dan Kontra dari dua jenis pengambilan ini.

Lazy initialization meningkatkan kinerja dengan menghindari perhitungan yang tidak perlu dan mengurangi kebutuhan memori.

Eager initialization membutuhkan lebih banyak konsumsi memori dan kecepatan pemrosesan lambat.

Karena itu, tergantung pada situasi salah satu inisialisasi ini dapat digunakan.


1
Pernyataan bahwa "tidak memuat hubungan kecuali Anda memintanya melalui metode pengambil" adalah penting untuk dicatat, dan juga keputusan desain yang terbelakang menurut pendapat saya ... Saya baru saja menemukan sebuah kasus di mana saya berasumsi akan mengambilnya pada akses dan tidak, karena saya tidak memanggil fungsi getter untuk itu. Ngomong-ngomong, apa yang merupakan fungsi "pengambil"? Apakah JPA akan menunda memuat properti sampai fungsi yang dipanggil getMemberdipanggil yang persis sesuai dengan pola nama anggota?
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();
        }
    }

    }

Periksa metode ambil () dari Main.java. Ketika kita mendapatkan Subjek, maka daftar koleksi Buku , yang dijelaskan dengan @OneToMany, akan dimuat dengan malas. Tetapi, di sisi lain, Buku-buku yang berhubungan dengan asosiasi subjek koleksi , dijelaskan dengan @ManyToOne, memuat lebih banyak (oleh [default][1]untuk @ManyToOne, fetchType=EAGER). Kita dapat mengubah perilaku dengan menempatkan fetchType.EAGER di @OneToManySubject.java atau fetchType.LAZY di @ManyToOnedalam Books.java.


1

public enum FetchType meluas java.lang.Enum Menentukan strategi untuk mengambil data dari database. Strategi EAGER adalah persyaratan pada runtime penyedia persistensi bahwa data harus diambil dengan penuh semangat. Strategi LAZY adalah petunjuk untuk runtime penyedia persistensi bahwa data harus diambil dengan malas ketika pertama kali diakses. Implementasi diizinkan untuk mengambil data dengan penuh semangat yang petunjuk strategi LAZY telah ditentukan. Contoh: @Basic (fetch = LAZY) protected getName () {name return; }

Sumber


1

Saya ingin menambahkan catatan ini pada apa yang dikatakan "Kyung Hwan Min" di atas.

Misalkan Anda menggunakan Spring Rest dengan arsitek sederhana ini:

Controller <-> Layanan <-> Repositori

Dan Anda ingin mengembalikan beberapa data ke front-end, jika Anda menggunakan FetchType.LAZY, Anda akan mendapatkan pengecualian setelah Anda mengembalikan data ke metode pengontrol karena sesi ditutup di Layanan sehinggaJSON Mapper Object tidak bisa mendapatkan data.

Ada tiga opsi umum untuk mengatasi masalah ini, tergantung pada desain, kinerja, dan pengembang:

  1. Yang paling mudah adalah menggunakan FetchType.EAGER , Sehingga sesi akan tetap hidup pada metode controller.
  2. Anti-polaSolusi , untuk membuat sesi langsung sampai eksekusi berakhir, itu membuat masalah kinerja yang sangat besar dalam sistem.
  3. Praktik terbaik adalah menggunakan FetchType.LAZYmetode konverter untuk mentransfer data dari Entityke objek data lain DTOdan mengirimkannya ke pengontrol, sehingga tidak ada pengecualian jika sesi ditutup.

1

Hai, saya telah melampirkan 2 foto untuk membantu Anda memahami hal ini. masukkan deskripsi gambar di sini

masukkan deskripsi gambar di sini


0

@ drop-shadow jika Anda menggunakan Hibernate, Anda dapat memanggil Hibernate.initialize()ketika Anda memanggil getStudents()metode:

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: Itu mengambil entitas anak dengan malas, yaitu pada saat mengambil entitas induk, ia hanya mengambil proxy (dibuat oleh cglib atau utilitas lain) dari entitas anak dan ketika Anda mengakses properti entitas anak maka sebenarnya diambil oleh hibernate.

EAGER: itu mengambil entitas anak bersama dengan orang tua.

Untuk pemahaman yang lebih baik, buka dokumentasi Jboss atau Anda dapat menggunakan hibernate.show_sql=trueuntuk aplikasi Anda dan memeriksa pertanyaan yang dikeluarkan oleh hibernate.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.