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


552

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

차이 무엇 FetchType.LAZY과 FetchType.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) 주위에 프록시를 생성하여 구현됩니다 . 당신의 게으른 모음 그래서, 구체적인 유형은하지 ArrayList와 HashSet있지만, PersistentSet및 PersistentList(또는 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.LAZY와 FetchType.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.