Java Persistence API에서 FetchType LAZY와 EAGER의 차이점은 무엇입니까?
Java Persistence API 및 Hibernate의 초보자입니다.
차이 무엇 FetchType.LAZY
과 FetchType.EAGER
자바 퍼시스턴스 API 인은?
때로는 두 개의 엔티티가 있고 그들 사이에 관계가 있습니다. 예를 들어,이라는 엔터티 University
와 라는 다른 엔터티가 Student
있고 대학교에는 많은 학생이있을 수 있습니다.
대학교 단체는 아이디, 이름, 주소 등과 같은 몇 가지 기본 속성과 특정 대학교의 학생 목록을 반환하는 학생이라는 수집 속성을 가질 수 있습니다.
public class University {
private String id;
private String name;
private String address;
private List<Student> students;
// setters and getters
}
이제 데이터베이스에서 University를로드하면 JPA가 해당 ID, 이름 및 주소 필드를로드합니다. 그러나 학생들을 어떻게로드해야하는지에 대한 두 가지 옵션이 있습니다.
- 나머지 필드와 함께 (즉, 간절히)로드하거나
- 대학의
getStudents()
방법 을 호출 할 때 필요에 따라 (즉, 게으르게)로드합니다 .
대학에 많은 학생들이있을 경우, 특히 필요하지 않을 때 모든 학생들을 함께 불러오는 것이 비효율적이며, 그러한 경우에는 실제로 필요할 때 학생들을 불러 들이기를 원한다고 선언 할 수 있습니다. 이것을 게으른 로딩이라고합니다.
다음 students
은 간절히로드되도록 명시 적으로 표시된 예입니다 .
@Entity
public class University {
@Id
private String id;
private String name;
private String address;
@OneToMany(fetch = FetchType.EAGER)
private List<Student> students;
// etc.
}
다음 students
은 명시 적으로 게으르게로드되도록 표시된 예제 입니다.
@Entity
public class University {
@Id
private String id;
private String name;
private String address;
@OneToMany(fetch = FetchType.LAZY)
private List<Student> students;
// etc.
}
원래,
LAZY = fetch when needed
EAGER = fetch immediately
EAGER
컬렉션을로드한다는 것은 부모가 페치 될 때 완전히 페치되었음을 의미합니다. 당신이 가지고 Course
있고 가지고 있다면 List<Student>
, 모든 학생들은 데이터베이스 를 가져올 때 데이터베이스 에서 Course
가져옵니다.
LAZY
반면 List
에는 액세스하려는 경우에만 의 내용을 가져옵니다. 예를 들어을 호출하여 course.getStudents().iterator()
. 에서 액세스 방법을 List
호출하면 요소를 검색하기 위해 데이터베이스에 대한 호출이 시작됩니다. 이것은 List
(또는 Set
) 주위에 프록시를 생성하여 구현됩니다 . 당신의 게으른 모음 그래서, 구체적인 유형은하지 ArrayList
와 HashSet
있지만, PersistentSet
및 PersistentList
(또는 PersistentBag
)
성능 및 메모리 사용률을 고려할 수 있습니다. 한 가지 큰 차이점은 EAGER 페치 전략을 통해 페치 된 데이터 오브젝트를 세션없이 사용할 수 있다는 것입니다. 왜?
세션이 연결될 때 오브젝트에 표시된 데이터를 열망하면 모든 데이터가 페치됩니다. 그러나 지연로드 전략의 경우, 세션이 연결 해제 된 경우 ( session.close()
명령문 이후 ) 지연로드 표시 오브젝트는 데이터를 검색하지 않습니다 . 최대 절전 모드 프록시로 만들 수있는 모든 것. Eager 전략을 사용하면 세션을 닫은 후에도 데이터를 계속 사용할 수 있습니다.
기본적으로 모든 콜렉션 및 맵 오브젝트의 페치 규칙은 FetchType.LAZY
다른 규칙의 경우 FetchType.EAGER
정책을 따릅니다 .
간단히, @OneToMany
그리고 @ManyToMany
관계는 implicictly 관련 객체 (수집 및지도)를 가져 오지 않고 검색 작업을에서 필드를 통해 직렬로 연결된 @OneToOne
과 @ManyToOne
사람.
내 지식에 따라 두 가지 유형의 가져 오기가 요구 사항에 따라 다릅니다.
FetchType.LAZY
요청시 (즉, 데이터가 필요할 때)
FetchType.EAGER
(즉, 요구 사항이 나오기 전에 불필요하게 레코드를 가져옵니다)
모두 FetchType.LAZY
와 FetchType.EAGER
정의하는 데 사용하는 기본 계획을 가져 .
불행히도 LAZY 페치에 대한 기본 페치 계획 만 대체 할 수 있습니다. EAGER 가져 오기는 유연성이 떨어지고 많은 성능 문제가 발생할 수 있습니다 .
가져 오기는 쿼리 시간 책임이므로 연결을 EAGER로 만드는 충동을 억제하는 것이 좋습니다. 따라서 모든 쿼리는 fetch 지시문을 사용 하여 현재 비즈니스 사례에 필요한 내용 만 검색해야합니다.
로부터 자바 독 :
EAGER 전략은 데이터를 열심히 가져와야하는 지속성 제공자 런타임의 요구 사항입니다. LAZY 전략은 데이터가 처음 액세스 될 때 느리게 페치되어야한다는 지속성 제공자 런타임의 힌트입니다.
예를 들어, 열망하는 것이 게으른 것보다 더 적극적입니다. 게으른 사람은 처음 사용할 때만 (제공자가 힌트를 얻는 경우) 발생하지만 열망하는 일이 있으면 미리 가져올 수 있습니다.
Lazy
가져 오기 유형은 명시 적으로 표시하지 않는 한 Hibernate에 의해 선택된 디폴트로 Eager
유형을 가져옵니다. 보다 정확하고 간결하게하기 위해 다음과 같이 차이점을 설명 할 수 있습니다.
FetchType.LAZY
= getter 메소드를 통해 호출하지 않으면 관계를로드하지 않습니다.
FetchType.EAGER
= 모든 관계를로드합니다.
이 두 가지 인출 유형의 장단점.
Lazy initialization
불필요한 계산을 피하고 메모리 요구량을 줄임으로써 성능을 향상시킵니다.
Eager initialization
더 많은 메모리를 소비하고 처리 속도가 느립니다.
그런 말을하는 데, 상황에 따라 하나이 초기화 중 하나를 사용할 수 있습니다.
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의 retrieve () 메소드를 확인하십시오. Subject를 받으면 으로 주석이 달린 콜렉션 listBooks@OneToMany
가 느리게로드됩니다. 그러나, 다른 한편으로는, 책 수집의 관계와 관련된 주제 로 주석, @ManyToOne
, eargerly로드 (기준 [default][1]
를 들어 @ManyToOne
, fetchType=EAGER
). @OneToMany
Subject.java 에 fetchType.EAGER를 배치 하거나 Books.java에 fetchType.LAZY를 배치하여 동작을 변경할 수 있습니다 @ManyToOne
.
public enum FetchType extends java.lang.Enum 데이터베이스에서 데이터를 가져 오기위한 전략을 정의합니다. EAGER 전략은 데이터를 열심히 가져와야하는 지속성 제공자 런타임의 요구 사항입니다. LAZY 전략은 데이터가 처음 액세스 될 때 느리게 페치되어야한다는 지속성 제공자 런타임의 힌트입니다. 구현시 LAZY 전략 힌트가 지정된 데이터를 열심히 가져올 수 있습니다. 예 : @Basic (fetch = LAZY) 보호 문자열 getName () {return name; }
위의 "경환 민"의 말에이 메모를 추가하고 싶습니다.
이 간단한 설계자와 함께 Spring Rest를 사용한다고 가정하십시오.
컨트롤러 <-> 서비스 <-> 리포지토리
을 사용하는 경우 일부 데이터를 프런트 엔드로 FetchType.LAZY
반환하려고합니다. 서비스에서 세션이 닫혀 JSON Mapper Object
데이터를 가져올 수 없으므로 컨트롤러 메서드로 데이터를 반환 한 후 예외가 발생 합니다.
디자인, 성능 및 개발자에 따라이 문제를 해결하기위한 세 가지 공통 옵션이 있습니다.
- 가장 쉬운 방법은을 사용
FetchType.EAGER
하는 것이므로 세션이 컨트롤러 메소드에서 계속 활성화됩니다. - 안티 패턴 솔루션, 실행이 끝날 때까지 세션을 라이브로 만들려면 시스템에서 큰 성능 문제가 발생합니다.
- 가장 좋은 방법은
FetchType.LAZY
변환기 방법과 함께 사용 하여 데이터Entity
를 다른 데이터 개체DTO
로 전송하고 컨트롤러로 전송하는 것입니다. 따라서 세션이 종료 된 경우에도 예외가 없습니다.
@ drop-shadow Hibernate를 사용 Hibernate.initialize()
하는 경우 getStudents()
메소드 를 호출 할 때 호출 할 수 있습니다 .
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 : 자식 엔터티를 느리게 가져옵니다. 즉, 부모 엔터티를 가져올 때 자식 엔터티의 프록시 (cglib 또는 다른 유틸리티로 생성)를 가져 오며 자식 엔터티의 속성에 액세스하면 실제로 최대 절전 모드로 가져옵니다.
EAGER : 상위 엔티티와 함께 하위 엔티티를 가져옵니다.
이해를 돕기 위해 Jboss 문서로 이동하거나 hibernate.show_sql=true
앱에 사용 하고 최대 절전 모드에서 발행 한 쿼리를 확인할 수 있습니다 .
'development' 카테고리의 다른 글
.on ( 'click')과 .click ()의 차이점 (0) | 2020.02.12 |
---|---|
Java String을 byte []로 변환하는 방법? (0) | 2020.02.12 |
소수점 이하 두 자리까지 숫자 표시 (0) | 2020.02.12 |
부분 및 템플릿의 복잡한 중첩 (0) | 2020.02.12 |
모든 서버 측 코드에 대해 ConfigureAwait를 호출하는 모범 사례 (0) | 2020.02.12 |