development

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

big-blog 2020. 2. 12. 23:41
반응형

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


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

차이 무엇 FetchType.LAZYFetchType.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, 이름 및 주소 필드를로드합니다. 그러나 학생들을 어떻게로드해야하는지에 대한 두 가지 옵션이 있습니다.

  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.
}

원래,

LAZY = fetch when needed
EAGER = fetch immediately

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

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


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


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

(제공 :-objectdbcom)


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

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

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


모두 FetchType.LAZYFetchType.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). @OneToManySubject.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데이터를 가져올 수 없으므로 컨트롤러 메서드로 데이터를 반환 한 후 예외가 발생 합니다.

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

  1. 가장 쉬운 방법은을 사용 FetchType.EAGER하는 것이므로 세션이 컨트롤러 메소드에서 계속 활성화됩니다.
  2. 안티 패턴 솔루션, 실행이 끝날 때까지 세션을 라이브로 만들려면 시스템에서 큰 성능 문제가 발생합니다.
  3. 가장 좋은 방법은 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앱에 사용 하고 최대 절전 모드에서 발행 한 쿼리를 확인할 수 있습니다 .

참고 : https://stackoverflow.com/questions/2990799/difference-between-fetchtype-lazy-and-eager-in-java-persistence-api



반응형