web-dev-qa-db-ja.com

Java Persistence APIにおけるFetchType LAZYとEAGERの違いは?

私はJava Persistence APIとHibernateの初心者です。

Java Persistence APIの FetchType.LAZYFetchType.EAGER の違いは何ですか?

463
leon

時にはあなたは2つのエンティティを持ち、それらの間に関係があります。たとえば、UniversityというエンティティとStudentという別のエンティティがあるとします。

Universityエンティティには、id、name、addressなどの基本的なプロパティのほかに、Studentsというプロパティもあります。

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

 // setters and getters
}

データベースから大学をロードすると、JPAはそのid、name、およびaddressフィールドを自動的にロードします。しかし、学生には2つの選択肢があります。それを残りのフィールドと一緒に(つまり熱心に)ロードするか、大学のgetStudents()メソッドを呼び出すときにオンデマンドで(つまり遅延的に)ロードすることです。

大学に多くの学生がいるとき、彼らが必要とされていないときにそれをすべての学生にロードすることは効率的ではありません。したがって、そのような場合には、実際に必要なときに生徒に負荷をかけることを宣言できます。これは遅延ロードと呼ばれます。

898
Behrang

基本的に、

LAZY = fetch when needed
EAGER = fetch immediately
246
unbeli

コレクションのEAGERロードは、それらの親がフェッチされる時点でそれらが完全にフェッチされることを意味します。あなたがCourseを持っていて、それがList<Student>を持っている場合、すべての生徒は データベースから Courseがフェッチされた時点でフェッチされます。

一方、LAZYは、Listの内容がアクセスしようとしたときにのみ取得されることを意味します。たとえば、course.getStudents().iterator()を呼び出すことによって。 Listで任意のアクセス方法を呼び出すと、要素を取得するためにデータベースへの呼び出しが開始されます。これはList(またはSet)の周囲にProxyを作成することによって実装されます。だからあなたの怠惰なコレクションのために、具象型はArrayListHashSetではなく、PersistentSetPersistentList(またはPersistentBag)です。

59
Bozho

パフォーマンスとメモリ使用率を検討します。大きな違いの1つは、EAGERフェッチ戦略によって、セッションなしでフェッチされたデータオブジェクトを使用できることです。どうして?
セッションが接続されているときにオブジェクト内のマーク付きデータを積極的に取得すると、すべてのデータが取得されます。ただし、遅延ローディング戦略の場合、セッションが切断されていると(session.close()ステートメントの後)、遅延ローディングされたオブジェクトはデータを取得しません。すべて休止状態のプロキシによって行うことができます。積極的な戦略により、セッション終了後もデータを利用できるようになります。

13
Kyung Hwan Min

デフォルトでは、すべてのコレクションオブジェクトとマップオブジェクトでフェッチルールはFetchType.LAZYであり、それ以外のインスタンスではFetchType.EAGERポリシーに従います。
簡単に言うと、@OneToMany@ManyToManyの関係は暗黙のうちに関連するオブジェクト(コレクションとマップ)を取得するわけではありませんが、検索操作は@OneToOne@ManyToOneの中のフィールドを通してカスケードされます。

(提供: - objectdbcom)

11
babai

私の知る限りでは、両方のタイプのフェッチはあなたの要求次第です。

FetchType.LAZYはオンデマンドです(つまり、データが必要な場合)。

FetchType.EAGERは即時です(つまり、要求が来る前に不必要にレコードを取得しています)

10

FetchType.LAZYFetchType.EAGERは両方とも デフォルトのフェッチプラン を定義するために使用されます。

残念ながら、あなたはLAZYフェッチのためのデフォルトのフェッチプランしか上書きできません。 EAGERフェッチは柔軟性が低く、 多くのパフォーマンス問題を引き起こす可能性があります

フェッチは問い合わせ時の責任であるため、私のアドバイスはあなたのアソシエーションをEAGERにすることの衝動を抑えることです。そのため、すべてのクエリで、 fetch ディレクティブを使用して、現在のビジネスケースに必要なものだけを取得するようにしてください。

9
Vlad Mihalcea

Javadoc :から

EAGER戦略は、データを積極的に取得する必要があることを永続プロバイダーランタイムに課す要件です。 LAZY戦略は、データが最初にアクセスされたときに遅延してフェッチされるべきであるという永続プロバイダーランタイムへのヒントです。

例えば、熱心な人は怠け者よりも積極的です。 (プロバイダがヒントを得た場合)レイジーは初回使用時にのみ発生しますが、熱心なものではプリフェッチされる場合があります。

6
T.J. Crowder

明示的にLazyフェッチタイプを指定しない限り、EagerフェッチタイプはデフォルトでHibernateによって選択されます。より正確で簡潔にするために、違いは以下のように述べることができます。

FetchType.LAZY =これはgetterメソッドで呼び出さない限りリレーションシップをロードしません。

FetchType.EAGER =これは全ての関係をロードします。

これら2つのフェッチタイプの長所と短所。

Lazy initializationは、不要な計算を回避し、メモリ要件を減らすことでパフォーマンスを向上させます。

Eager initializationはより多くのメモリを消費し、処理速度は遅くなります。

とは言っても、 状況によります /これらの初期化のどちらかを使用できます。

4
Dulith De Costa

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でアノテーションが付けられたBooks関連のcollection subject の関連付けは、([default][1]に対して@ManyToOnefetchType=EAGERによって)早くロードされます。 Books.Javaでは、fetchType.EAGERを@OneToMany Subject.Javaに、またはfetchType.LAZYを@ManyToOneに配置することで、動作を変更できます。

3
Deepak

public enum FetchTypeはJava.lang.Enumを拡張したものです。データベースからデータを取得するための戦略を定義します。 EAGER戦略は、データを積極的に取得する必要があることを永続プロバイダーランタイムに課す要件です。 LAZY戦略は、データが最初にアクセスされたときに遅延してフェッチされるべきであるという永続プロバイダーランタイムへのヒントです。実装は、LAZY戦略ヒントが指定されているデータを熱心に取得することを許可されています。例:@Basic(fetch = LAZY)protected String getName(){戻り名; }

出典

1
Anas Lachheb

上記の「キョンファンミン」が言ったことにこのメモを加えたい。

この単純なアーキテクトでSpring Restを使用しているとします:

コントローラ<->サービス<->リポジトリ

また、FetchType.LAZYを使用している場合、一部のデータをフロントエンドに返したい場合、サービスでセッションが閉じられるため、コントローラーメソッドにデータを返した後、JSON Mapper Objectデータを取得できません。

この問題を解決するための3つの一般的なオプションがあり、設計、パフォーマンス、および開発者によって異なります。

  1. 最も簡単な方法は、FetchType.EAGERを使用することです。これにより、セッションはコントローラーメソッドで引き続き有効になります。
  2. Anti-patterns ソリューション、実行が終了するまでセッションを有効にするには、システムのパフォーマンスに大きな問題が生じます。
  3. ベストプラクティスは、FetchType.LAZYをコンバーターメソッドで使用してEntityから別のデータオブジェクトDTOにデータを転送し、コントローラーに送信することです。したがって、セッションが閉じられても例外はありません。
0

Hibernateを使用している場合は、@ drop-shadowを使用します。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
Jules Martel

LAZY:子エンティティを遅延的に取得します。つまり、親エンティティの取得時には、子エンティティのプロキシ(cglibまたは他のユーティリティによって作成されたもの)を取得するだけです。

EAGER:それは親と共に子エンティティを取得します。

よりよく理解するためにJbossのドキュメントを参照するか、あなたのアプリケーションにhibernate.show_sql=trueを使用してHibernateによって発行されたクエリをチェックすることができます。

0
user1157635