Saya seorang pemula untuk Java Persistence API dan Hibernate.
Apa perbedaan antara FetchType.LAZY
dan FetchType.EAGER
di Java Persistence API?
Saya seorang pemula untuk Java Persistence API dan Hibernate.
Apa perbedaan antara FetchType.LAZY
dan FetchType.EAGER
di Java Persistence API?
Jawaban:
Terkadang Anda memiliki dua entitas dan ada hubungan di antara mereka. Misalnya, Anda mungkin memiliki entitas yang dipanggil University
dan entitas lain yang dipanggil Student
dan 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:
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:
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 students
ditandai 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 students
secara 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.
}
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.
getStudents()
metode untuk pertama kalinya, apakah hasilnya di-cache? sehingga saya dapat mengakses hasil tersebut lebih cepat di waktu berikutnya?
EAGER
memuat koleksi berarti bahwa mereka diambil sepenuhnya pada saat orang tua mereka diambil. Jadi jika sudah Course
dan sudah List<Student>
, semua siswa diambil dari database pada saat Course
diambil.
LAZY
di sisi lain berarti bahwa konten List
diambil hanya ketika Anda mencoba mengaksesnya. Misalnya dengan menelepon course.getStudents().iterator()
. Memanggil metode akses apa pun pada List
akan 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 ArrayList
dan HashSet
, tapi PersistentSet
dan PersistentList
(atau PersistentBag
)
course.getStudents()
, ia meluncurkan query SQL (melihatnya di konsol). Dalam jenis pengambilan Malas juga, hal yang sama terjadi. Jadi, apa bedanya ??
fetchtype = LAZY
default bahkan jika mencoba untuk mendapatkan koleksi dengan pengambil hibernete melempar kesalahan mengatakan saya itu tidak dapat mengevaluasi
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.
Secara default, untuk semua objek koleksi dan peta, aturan pengambilannya adalah FetchType.LAZY
dan untuk kasus lain, ia mengikuti FetchType.EAGER
kebijakan.
Singkatnya, @OneToMany
dan @ManyToMany
relasi tidak mengambil objek terkait (pengumpulan dan peta) secara implisit tetapi operasi pengambilan mengalir melalui bidang @OneToOne
dan objek @ManyToOne
.
Keduanya FetchType.LAZY
dan FetchType.EAGER
digunakan 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.
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.
The Lazy
Fetch jenis adalah secara default dipilih oleh Hibernate kecuali Anda secara eksplisit menandai Eager
Ambil 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.
getMember
dipanggil yang persis sesuai dengan pola nama anggota?
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 @OneToMany
Subject.java atau fetchType.LAZY di @ManyToOne
dalam Books.java.
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; }
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:
FetchType.EAGER
, Sehingga sesi akan tetap hidup pada metode controller.FetchType.LAZY
metode konverter untuk mentransfer data dari Entity
ke objek data lain DTO
dan mengirimkannya ke pengontrol, sehingga tidak ada pengecualian jika sesi ditutup.@ 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;
}
//...
}
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=true
untuk aplikasi Anda dan memeriksa pertanyaan yang dikeluarkan oleh hibernate.