Java Persistence API에서 FetchType LAZY와 EAGER의 차이점은 무엇입니까?


552

Java Persistence API 및 Hibernate의 초보자입니다.

차이 무엇 FetchType.LAZYFetchType.EAGER자바 퍼시스턴스 API 인은?


1
컬렉션의 EAGER 로딩은 부모가 페치 될 때 완전히 페치됨을 의미합니다. EAGER가로드되는 동안 내 모든 자식을 가져옵니다. 하위는 Persistent Bag 내부의 PersistentSet 및 PersistentList (또는 PersistentBag)에서 페치되어 배열 목록으로 표시됩니다. 맞
습니까

답변:


1064

때로는 두 개의 엔티티가 있고 그들 사이에 관계가 있습니다. 예를 들어,이라는 엔터티 University와 라는 다른 엔터티가 Student있고 대학교에는 많은 학생이있을 수 있습니다.

대학교 단체는 아이디, 이름, 주소 등과 같은 몇 가지 기본 속성과 특정 대학교의 학생 목록을 반환하는 학생이라는 수집 속성을 가질 수 있습니다.

대학에는 많은 학생들이 있습니다

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

   // setters and getters
}

이제 데이터베이스에서 University를로드하면 JPA가 해당 ID, 이름 및 주소 필드를로드합니다. 그러나 학생들을 어떻게로드해야하는지에 대한 두 가지 옵션이 있습니다.

  1. 나머지 필드와 함께 (즉, 간절히)로드하거나
  2. 대학의 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.
}

5
@BehrangSaeedzadeh 당신이 언급 한 효율성을 제외하고는 실제적인 차이점이나 각 유형의 장단점의 장단점을 나열 할 수 있습니다. 왜 열성적인 로딩을 사용하고 싶습니까?
ADTC

73
@ADTC 게으른 로딩이 작동하려면 getter 메소드 (예 :)를 호출하여 대상 엔티티를 메모리에로드하려고 할 때 JDBC 세션이 계속 열려 있어야 getStudents()하지만이 방법으로 인해 시간이 걸리지 않는 경우도 있습니다. 이 호출되면 세션이 이미 닫히고 엔티티가 분리됩니다. 마찬가지로, 때때로 클라이언트 / 서버 아키텍처 (예 : Swing 클라이언트 / JEE 서버)가 있고 엔티티 / DTO가 유선으로 클라이언트로 전송되며, 이러한 시나리오에서는 엔티티의 방식으로 인해 지연 로딩이 작동하지 않는 경우가 종종 있습니다 와이어를 통해 직렬화됩니다.
TheFooProgrammer

4
내 책 에서이 답변에 더 많은 정보를 추가하고 싶습니다. 메모리를 절약하기 위해 지연로드는 일반적으로 일대 다 관계에 사용됩니다. 일대일로 일반적으로 Eager가 사용됩니다.
Erran Morad

2
지연 로딩에서 getStudents()메서드를 처음 호출 하면 결과가 캐시됩니까? 다음에 더 빨리 그 결과에 액세스 할 수 있도록?
JavaTechnical

2
@JavaTechnical은 두 번째 레벨 캐시를 활성화하는지 여부 (기본적으로 활성화 됨)
Ced

285

원래,

LAZY = fetch when needed
EAGER = fetch immediately

11
@Behang의 답변을 읽은 후에 만 ​​명확합니다. 명확한 요약 감사합니다. :-)
Nabin

66

EAGER컬렉션을로드한다는 것은 부모를 가져올 때 완전히 가져 오는 것을 의미합니다. 당신이 가지고 Course있고 그것을 가지고 있다면 List<Student>, 모든 학생들은 데이터베이스 를 가져올 때 데이터베이스 에서 Course가져옵니다.

LAZY반면 List에는 액세스하려는 경우에만 의 내용을 가져옵니다. 예를 들어을 호출하여 course.getStudents().iterator(). 에서 액세스 방법을 List호출하면 요소를 검색하기 위해 데이터베이스에 대한 호출이 시작됩니다. 이것은 List(또는 Set) 주위에 프록시를 생성하여 구현됩니다 . 당신의 게으른 모음 그래서, 구체적인 유형은하지 ArrayListHashSet있지만, PersistentSetPersistentList(또는 PersistentBag)


하위 엔티티의 세부 사항을 가져 오는 데이 개념을 사용했지만 그 차이점을 볼 수는 없습니다. Eager 가져 오기를 지정하면 모든 항목을 가져오고 디버깅 할 때 하위 엔티티에 "Bean deferred"가 표시됩니다. 내가 말하면 course.getStudents()SQL 쿼리를 실행합니다 (콘솔에서 보았습니다). Lazy fetch type에서도 같은 일이 발생합니다. 차이점은 무엇입니까 ??
Neha Choudhary

소유 엔티티가로드 될 때 열망하는 콜렉션이 페치됩니다. 지연 모음은 액세스 할 때 가져옵니다. 이 동작하여 톱없는 경우 (예를 들어, 클래스의 이전 버전을 실행하는) 아마 당신의 환경에 뭔가 잘못이 있었다
Bozho

1
@Bozho 컬렉션의 지연로드 만 지정했습니다. 간단한 문자열 필드를 지연로드 할 수 있습니까?
vikiiii September

아니요. 열의 하위 집합을 가져 오려면 쿼리 또는 다른 매핑 된 엔터티를 사용해야합니다.
Bozho

@Bozho, 안녕하세요 fetchtype = LAZYgetter hibernete로 컬렉션을 가져 오려고 시도해도 평가할 수 없다는 오류가 발생하더라도 기본 설정으로 설정 한 경우이 질문에 대답 해 주시겠습니까?
Все Едно

16

성능 및 메모리 사용률을 고려할 수 있습니다. 한 가지 큰 차이점은 EAGER 페치 전략으로 세션없이 페치 된 데이터 오브젝트를 사용할 수 있다는 것입니다. 왜?
세션이 연결될 때 오브젝트에 표시된 데이터를 열망하면 모든 데이터가 페치됩니다. 그러나 지연로드 전략의 경우, 세션이 연결 해제 된 경우 ( session.close()명령문 이후 ) 지연로드 표시 오브젝트는 데이터를 검색하지 않습니다 . 최대 절전 모드 프록시로 만들 수있는 모든 것. Eager 전략을 사용하면 세션을 닫은 후에도 데이터를 계속 사용할 수 있습니다.


11

내 지식에 따라 두 가지 유형의 가져 오기가 요구 사항에 따라 다릅니다.

FetchType.LAZY 요청시 (즉, 데이터가 필요할 때)

FetchType.EAGER (즉, 요구 사항이 나오기 전에 불필요하게 레코드를 가져옵니다)


11

기본적으로 모든 콜렉션 및 맵 오브젝트의 페치 규칙은 FetchType.LAZY다른 규칙의 경우 FetchType.EAGER정책을 따릅니다 .
간단히, @OneToMany그리고 @ManyToMany관계는 implicictly 관련 객체 (수집 및지도)를 가져 오지 않고 검색 작업을에서 필드를 통해 직렬로 연결된 @OneToOne@ManyToOne사람.

(제공 :-objectdbcom)


9

모두 FetchType.LAZYFetchType.EAGER정의하는 데 사용하는 기본 계획을 가져 .

불행히도 LAZY 페치에 대한 기본 페치 계획 만 대체 할 수 있습니다. EAGER 가져 오기는 유연성이 떨어지고 많은 성능 문제가 발생할 수 있습니다 .

가져 오기는 쿼리 시간 책임이므로 연결을 EAGER로 만드는 충동을 억제하는 것이 좋습니다. 따라서 모든 쿼리는 fetch 지시문을 사용 하여 현재 비즈니스 사례에 필요한 항목 만 검색해야합니다.


2
"EAGER 가져 오기는 유연성이 떨어지고 많은 성능 문제가 발생할 수 있습니다." ... "EAGER 페칭을 사용하거나 사용하지 않으면 성능 문제가 발생할 수 있습니다." 지연된 초기화 필드가 액세스하는 데 비용이 많이 들고 자주 사용되지 않는 경우에는 지연 페칭이 성능에 도움이됩니다. 그러나 변수를 자주 사용하는 경우 지연 초기화는 초기 초기화보다 데이터베이스에 더 많은 트립이 필요하므로 실제로 성능저하 될 수 있습니다 . 교리 적으로가 아니라 FetchType을 올바르게 적용하는 것이 좋습니다.
scottb

여기서 책을 홍보하고 있습니까? 그러나 예, 유스 케이스 및 카디널리티 관계에서 참조되는 객체 크기에 따라 다릅니다.
John Doe

6

로부터 자바 독 :

EAGER 전략은 데이터를 열심히 가져와야하는 지속성 제공자 런타임의 요구 사항입니다. LAZY 전략은 데이터가 처음 액세스 될 때 느리게 페치되어야한다는 지속성 제공자 런타임에 대한 힌트입니다.

예를 들어, 열심이 게으른 것보다 더 적극적입니다. 게으른 사람은 처음 사용할 때만 (제공자가 힌트를 얻는 경우) 발생하지만 열망하는 일이 있으면 미리 가져올 수 있습니다.


1
"처음 사용"이란 무엇입니까?
레온

@leon : 열망하는 필드와 게으른 필드를 가진 엔티티가 있다고 가정하십시오. 엔티티를 가져 오면 엔티티 참조를 수신 할 때까지 열망 필드가 DB에서로드되지만 지연 필드가로드되지 않았을 수 있습니다. 접근 자를 통해 필드 에 액세스 하려고 할 때만 가져옵니다 .
TJ Crowder 2016 년

@TJ Crowder, 페치 유형이 정의되지 않은 경우 기본값은 무엇입니까?
Mahmoud Saleh

@MahmoudSaleh : 나는 모른다. 아마 뭔가에 따라 달라질 수 있습니다. 실제 프로젝트에서 JPA를 사용하지 않았기 때문에 JPA를 사용하지 않았습니다.
TJ Crowder

2
@MahmoudS : 기본 가져 오기 유형 : OneToMany : LAZY, ManyToOne : EAGER, ManyToMany : LAZY, OneToOne : EAGER, 열 :
EAGER

5

Lazy가져 오기 유형은 명시 적으로 표시하지 않는 한 Hibernate에 의해 선택된 디폴트로 Eager유형을 가져옵니다. 보다 정확하고 간결하게하기 위해 다음과 같이 차이점을 설명 할 수 있습니다.

FetchType.LAZY = getter 메소드를 통해 호출하지 않으면 관계를로드하지 않습니다.

FetchType.EAGER = 모든 관계를로드합니다.

이 두 가지 인출 유형의 장단점.

Lazy initialization 불필요한 계산을 피하고 메모리 요구량을 줄임으로써 성능을 향상시킵니다.

Eager initialization 더 많은 메모리를 소비하고 처리 속도가 느립니다.

그런 말을하는 데, 상황에 따라 하나이 초기화 중 하나를 사용할 수 있습니다.


1
"getter 메소드를 통해 호출하지 않는 한 관계를로드하지 않는다"는 진술은 중요하며 내 의견에 지연된 디자인 결정이 중요합니다 ... 액세스 가져 오는 것으로 가정 하고 명시 적으로 getter 함수를 호출하지 않았기 때문에 그렇지 않았습니다. 그런데 "게터"기능은 무엇입니까? JPA getMember는 멤버의 이름 패턴과 정확히 일치하는 호출 된 함수가 호출 될 때까지 특성로드를 연기 합니까?
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의 retrieve () 메소드를 확인하십시오. Subject를 받으면 으로 주석이 달린 컬렉션 listBooks@OneToMany 가 느리게로드됩니다. 수집의하지만, 다른 한편으로는, 책 관련 협회 주제 로 주석, @ManyToOne에 의해 eargerly로드 ( [default][1]대한@ManyToOne , fetchType=EAGER). @OneToManySubject.java 에 fetchType.EAGER를 배치 하거나 Books.java에 fetchType.LAZY를 배치하여 동작을 변경할 수 있습니다 @ManyToOne.


1

public enum FetchType extends java.lang.Enum 데이터베이스에서 데이터를 가져 오기위한 전략을 정의합니다. EAGER 전략은 데이터를 열심히 가져와야하는 지속성 제공자 런타임의 요구 사항입니다. LAZY 전략은 데이터가 처음 액세스 될 때 느리게 페치되어야한다는 지속성 제공자 런타임에 대한 힌트입니다. 구현시 LAZY 전략 힌트가 지정된 데이터를 열심히 가져올 수 있습니다. 예 : @Basic (fetch = LAZY) 보호 문자열 getName () {return name; }

출처


1

위의 "경환 민"의 말에이 메모를 추가하고 싶습니다.

이 간단한 설계자와 함께 Spring Rest를 사용한다고 가정하십시오.

컨트롤러 <-> 서비스 <-> 리포지토리

을 사용하는 경우 일부 데이터를 프런트 엔드로 FetchType.LAZY반환하려고합니다. 서비스에서 세션이 닫혀 있으므로 컨트롤러 메소드로 데이터를 반환하면 예외가 발생합니다.JSON Mapper Object 데이터를 가져올 수 없으므로 합니다.

디자인, 성능 및 개발자에 따라이 문제를 해결하기위한 세 가지 공통 옵션이 있습니다.

  1. 가장 쉬운 방법은을 사용 FetchType.EAGER하는 것이므로 세션이 컨트롤러 메소드에서 계속 활성화됩니다.
  2. 안티 패턴 솔루션, 실행이 끝날 때까지 세션을 라이브로 만들려면 시스템에서 큰 성능 문제가 발생합니다.
  3. 가장 좋은 방법은 FetchType.LAZY변환기 방법과 함께 사용 하여 데이터 Entity를 다른 데이터 개체 DTO로 전송하고 컨트롤러로 전송하는 것입니다. 따라서 세션이 닫히더라도 예외는 없습니다.


0

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

0

LAZY : 자식 엔터티를 느리게 가져옵니다. 즉, 부모 엔터티를 가져올 때 자식 엔터티의 프록시 (cglib 또는 다른 유틸리티에서 생성)를 가져 오며 자식 엔터티의 속성에 액세스하면 실제로 최대 절전 모드로 가져옵니다.

EAGER : 상위 엔티티와 함께 ​​하위 엔티티를 가져옵니다.

이해를 돕기 위해 Jboss 문서로 이동하거나 hibernate.show_sql=true앱에 사용 하고 최대 절전 모드에서 발행 한 쿼리를 확인할 수 있습니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.