Tôi là người mới sử dụng API liên tục Java và Hibernate.
Sự khác biệt giữa FetchType.LAZY
và FetchType.EAGER
trong API liên tục Java là gì?
Tôi là người mới sử dụng API liên tục Java và Hibernate.
Sự khác biệt giữa FetchType.LAZY
và FetchType.EAGER
trong API liên tục Java là gì?
Câu trả lời:
Đôi khi bạn có hai thực thể và có một mối quan hệ giữa chúng. Ví dụ: bạn có thể có một thực thể được gọi University
và một thực thể khác được gọi Student
và một trường đại học có thể có nhiều Sinh viên:
Thực thể Đại học có thể có một số thuộc tính cơ bản như id, tên, địa chỉ, v.v. cũng như một thuộc tính bộ sưu tập được gọi là sinh viên trả về danh sách sinh viên cho một trường đại học nhất định:
public class University {
private String id;
private String name;
private String address;
private List<Student> students;
// setters and getters
}
Bây giờ khi bạn tải một trường đại học từ cơ sở dữ liệu, JPA sẽ tải các trường id, tên và địa chỉ của nó cho bạn. Nhưng bạn có hai lựa chọn về cách sinh viên nên được tải:
getStudents()
phương thức của trường đại học .Khi một trường đại học có nhiều sinh viên, việc tải tất cả các sinh viên cùng với nó là không hiệu quả, đặc biệt là khi họ không cần thiết và trong những trường hợp tương tự, bạn có thể tuyên bố rằng bạn muốn sinh viên được tải khi họ thực sự cần thiết. Điều này được gọi là lười tải.
Đây là một ví dụ, nơi students
được đánh dấu rõ ràng sẽ được tải một cách háo hức:
@Entity
public class University {
@Id
private String id;
private String name;
private String address;
@OneToMany(fetch = FetchType.EAGER)
private List<Student> students;
// etc.
}
Và đây là một ví dụ students
được đánh dấu rõ ràng để được tải một cách lười biếng:
@Entity
public class University {
@Id
private String id;
private String name;
private String address;
@OneToMany(fetch = FetchType.LAZY)
private List<Student> students;
// etc.
}
getStudents()
), nhưng đôi khi điều này là không thể, bởi vì tại thời điểm phương thức này được gọi, phiên đã đóng và thực thể tách ra. Tương tự, đôi khi chúng ta có kiến trúc máy khách / máy chủ (ví dụ: máy khách Swing / máy chủ JEE) và các thực thể / DTO được truyền qua dây cho máy khách và thường xuyên nhất trong các trường hợp tải lười biếng này sẽ không hoạt động do cách các thực thể được nối tiếp qua dây.
getStudents()
phương thức lần đầu tiên, các kết quả có được lưu trong bộ nhớ cache không? để lần sau tôi có thể truy cập những kết quả đó nhanh hơn?
EAGER
tải các bộ sưu tập có nghĩa là chúng được tìm nạp đầy đủ tại thời điểm cha mẹ của chúng được tìm nạp. Vì vậy, nếu bạn có Course
và nó có List<Student>
, tất cả các sinh viên được tìm nạp từ cơ sở dữ liệu tại thời điểm Course
được tìm nạp.
LAZY
mặt khác có nghĩa là nội dung của List
chỉ được tìm nạp khi bạn cố gắng truy cập chúng. Ví dụ, bằng cách gọi course.getStudents().iterator()
. Gọi bất kỳ phương thức truy cập nào trên List
sẽ bắt đầu một cuộc gọi đến cơ sở dữ liệu để lấy các phần tử. Điều này được thực hiện bằng cách tạo Proxy xung quanh List
(hoặc Set
). Vì vậy, các bộ sưu tập của bạn lười biếng, các loại bê tông không ArrayList
và HashSet
, nhưng PersistentSet
và PersistentList
(hoặc PersistentBag
)
course.getStudents()
, nó kích hoạt một truy vấn SQL (đã thấy trên bảng điều khiển). Trong kiểu tìm nạp Lazy cũng vậy, điều tương tự cũng xảy ra. Vì vậy, sự khác biệt là gì ??
fetchtype = LAZY
một trong những mặc định ngay cả khi cố gắng để có được bộ sưu tập với hibernete getter ném một lỗi nói với tôi nó không thể đánh giá
Tôi có thể xem xét hiệu suất và sử dụng bộ nhớ. Một điểm khác biệt lớn là chiến lược tìm nạp EAGER cho phép sử dụng đối tượng dữ liệu được tìm nạp mà không cần phiên. Tại sao?
Tất cả dữ liệu được tìm nạp khi dữ liệu được đánh dấu háo hức trong đối tượng khi phiên được kết nối. Tuy nhiên, trong trường hợp chiến lược tải lười biếng, lười biếng tải đối tượng được đánh dấu sẽ không truy xuất dữ liệu nếu phiên bị ngắt kết nối (sau session.close()
câu lệnh). Tất cả những gì có thể được thực hiện bởi proxy ngủ đông. Chiến lược háo hức cho phép dữ liệu vẫn có sẵn sau khi kết thúc phiên.
Theo mặc định, đối với tất cả các đối tượng bộ sưu tập và bản đồ, quy tắc tìm nạp là FetchType.LAZY
và đối với các trường hợp khác, nó tuân theo FetchType.EAGER
chính sách.
Tóm lại, @OneToMany
và các @ManyToMany
mối quan hệ không tìm nạp các đối tượng liên quan (bộ sưu tập và bản đồ) một cách rõ ràng nhưng hoạt động truy xuất được xếp tầng qua trường trong @OneToOne
và @ManyToOne
các đối tượng.
Cả hai FetchType.LAZY
và FetchType.EAGER
được sử dụng để xác định kế hoạch tìm nạp mặc định .
Thật không may, bạn chỉ có thể ghi đè gói tìm nạp mặc định cho tìm nạp LAZY. Tìm nạp EAGER kém linh hoạt và có thể dẫn đến nhiều vấn đề về hiệu suất .
Lời khuyên của tôi là kiềm chế sự thôi thúc làm cho các hiệp hội của bạn EAGER bởi vì việc tìm nạp là một trách nhiệm về thời gian truy vấn. Vì vậy, tất cả các truy vấn của bạn nên sử dụng chỉ thị tìm nạp để chỉ truy xuất những gì cần thiết cho trường hợp kinh doanh hiện tại.
Từ Javadoc :
Chiến lược EAGER là một yêu cầu đối với thời gian chạy của nhà cung cấp kiên trì rằng dữ liệu phải được lấy một cách háo hức. Chiến lược LAZY là một gợi ý cho thời gian chạy của nhà cung cấp kiên trì rằng dữ liệu nên được tải xuống một cách lười biếng khi lần đầu tiên được truy cập.
Ví dụ, háo hức là chủ động hơn lười biếng. Lười biếng chỉ xảy ra khi sử dụng lần đầu (nếu nhà cung cấp đưa ra gợi ý), trong khi với những điều háo hức (có thể) được tải trước.
Các Lazy
Fetch loại là theo mặc định lựa chọn bởi Hibernate trừ khi bạn đánh dấu một cách rõ ràngEager
Fetch loại. Để chính xác hơn và súc tích, sự khác biệt có thể được nêu ra dưới đây.
FetchType.LAZY
= Điều này không tải các mối quan hệ trừ khi bạn gọi nó thông qua phương thức getter.
FetchType.EAGER
= Điều này tải tất cả các mối quan hệ.
Ưu và nhược điểm của hai loại tìm nạp này.
Lazy initialization
cải thiện hiệu suất bằng cách tránh tính toán không cần thiết và giảm yêu cầu bộ nhớ.
Eager initialization
tiêu thụ nhiều bộ nhớ hơn và tốc độ xử lý chậm.
Có nói rằng, tùy thuộc vào tình huống một trong những khởi tạo này có thể được sử dụng.
getMember
được gọi chính xác khớp với mẫu tên của thành viên?
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;
}
}
Tiêu đề
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();
}
}
}
Kiểm tra phương thức lấy () của Main.java. Khi chúng tôi nhận đề, sau đó bộ sưu tập của mình listBooks , chú thích với @OneToMany
, sẽ được nạp một cách lười biếng. Tuy nhiên, mặt khác, các hiệp hội liên quan đến Sách của chủ đề bộ sưu tập , được chú thích @ManyToOne
, tải nhanh chóng (bởi [default][1]
for @ManyToOne
, fetchType=EAGER
). Chúng tôi có thể thay đổi hành vi bằng cách đặt fetchType.EAGER trên @OneToMany
Subject.java hoặc fetchType.LAZY trên @ManyToOne
Books.java.
chung enum FetchType mở rộng java.lang.Enum Xác định các chiến lược để tìm nạp dữ liệu từ cơ sở dữ liệu. Chiến lược EAGER là một yêu cầu đối với thời gian chạy của nhà cung cấp kiên trì rằng dữ liệu phải được lấy một cách háo hức. Chiến lược LAZY là một gợi ý cho thời gian chạy của nhà cung cấp kiên trì rằng dữ liệu nên được tải xuống một cách lười biếng khi lần đầu tiên được truy cập. Việc triển khai được phép lấy dữ liệu một cách háo hức mà gợi ý chiến lược LAZY đã được chỉ định. Ví dụ: @Basic (fetch = LAZY) Chuỗi được bảo vệ getName () {tên trả về; }
Tôi muốn thêm ghi chú này vào những gì "Kyung Hwan Min" đã nói ở trên.
Giả sử bạn đang sử dụng Spring Rest với kiến trúc sư đơn giản này:
Trình điều khiển <-> Dịch vụ <-> Kho lưu trữ
Và bạn muốn trả lại một số dữ liệu cho giao diện người dùng, nếu bạn đang sử dụng FetchType.LAZY
, bạn sẽ có một ngoại lệ sau khi bạn trả lại dữ liệu cho phương thức điều khiển do phiên được đóng trong Dịch vụ để JSON Mapper Object
không thể lấy dữ liệu.
Có ba tùy chọn phổ biến để giải quyết vấn đề này, tùy thuộc vào thiết kế, hiệu suất và nhà phát triển:
FetchType.EAGER
, do đó phiên sẽ vẫn tồn tại ở phương thức điều khiển.FetchType.LAZY
với phương thức chuyển đổi để truyền dữ liệu từ Entity
đối tượng dữ liệu khác DTO
và gửi nó đến bộ điều khiển, do đó không có ngoại lệ nếu phiên đóng.@ drop-Shadow nếu bạn đang sử dụng Hibernate, bạn có thể gọi Hibernate.initialize()
khi bạn gọi getStudents()
phương thức:
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: Nó tìm nạp các thực thể con một cách lười biếng, tức là tại thời điểm tìm nạp thực thể cha mẹ, nó chỉ tìm nạp proxy (được tạo bởi cglib hoặc bất kỳ tiện ích nào khác) của các thực thể con và khi bạn truy cập vào bất kỳ thuộc tính nào của thực thể con thì nó thực sự được tìm nạp bởi chế độ ngủ đông.
EAGER: nó tìm nạp các thực thể con cùng với cha mẹ.
Để hiểu rõ hơn, hãy truy cập tài liệu của Jboss hoặc bạn có thể sử dụng hibernate.show_sql=true
cho ứng dụng của mình và kiểm tra các truy vấn do chế độ ngủ đông phát hành.