Java Persistence APIのFetchType LAZYとEAGERの違いは何ですか?


552

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

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


1
コレクションのEAGERロードは、親がフェッチされるときに完全にフェッチされることを意味します。EAGERの読み込み中に、すべての子がフェッチされます。子はPersistentSetとPersistentList(またはPersistentBag)でフェッチされ、Persistent Bag内で配列リストとして表示されます。正しいですか?? ..
geetha

回答:


1064

2つのエンティティがあり、それらの間に関係がある場合があります。たとえば、あるエンティティが呼び出されUniversity、別のエンティティが呼び出さStudentれ、大学には多くの学生がいる場合があります。

Universityエンティティには、id、name、addressなどの基本的なプロパティと、特定の大学の学生のリストを返すstudentというコレクションプロパティがある場合があります。

大学には学生が多い

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

   // setters and getters
}

これで、データベースから大学をロードすると、JPAがそのid、name、およびaddressフィールドをロードします。ただし、生徒の読み込み方法には2つのオプションがあります。

  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 2013

73
@ADTC遅延読み込みが機能するためには、ゲッターメソッド(例:)を呼び出してターゲットエンティティをメモリに読み込みたいときに、JDBCセッションを開いたままにする必要getStudents()がありますが、このメソッドの実行までに、これができない場合があります。が呼び出され、セッションはすでに閉じられており、エンティティは切り離されています。同様に、クライアント/サーバーアーキテクチャ(例:Swingクライアント/ JEEサーバー)があり、エンティティ/ DTOがネットワーク経由でクライアントに転送され、これらのシナリオでは、エンティティの方法が原因でレイジーロードが機能しないことがよくあります。ワイヤーを介してシリアル化されます。
TheFooProgrammer 2013

4
私の本からこの回答にいくつかの情報を追加したいと思います-メモリを節約するために、レイジーロードは一般的に1対多および多対多の関係に使用されます。1対1では、一般的にEagerが使用されます。
Erran Morad、2014

2
遅延読み込みで、getStudents()メソッドを初めて呼び出すと、結果はキャッシュされますか?次回はこれらの結果により速くアクセスできるようにするには?
JavaTechnical 2014年

2
@JavaTechnicalは、2次キャッシュ(デフォルトで有効)を有効にするかどうかに依存します
Ced

285

基本的に、

LAZY = fetch when needed
EAGER = fetch immediately

11
非常に明確ですが、@ Behangの回答を読んだ後のみです。明確な要約をありがとうございます。:-)
Nabin 2016年

66

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

LAZY一方、コンテンツListにアクセスしようとした場合にのみ、コンテンツがフェッチされることを意味します。たとえば、を呼び出しcourse.getStudents().iterator()ます。でアクセスメソッドをList呼び出すと、データベースを呼び出して要素を取得します。これは、List(またはSet)の周囲にプロキシを作成することで実装されます。したがって、遅延コレクションの具体的な型はArrayListand HashSetではなくPersistentSet、and PersistentList(またはPersistentBag)です。


子エンティティの詳細を取得する際にその概念を使用しましたが、それらの違いはわかりません。Eager fetchを指定すると、すべてがフェッチされ、デバッグすると、子エンティティで「Bean deferred」が表示されます。私が言うときcourse.getStudents()、それはSQLクエリを起動します(コンソールで見ました)。レイジーフェッチタイプでも同じことが起こります。それで、違いは何ですか?
Neha Choudhary 2013年

所有エンティティがロードされると、熱心なコレクションがフェッチされます。遅延コレクションは、アクセスするとフェッチされます。これがあなたの見た振る舞いではない場合、おそらく環境に問題があります(たとえば、クラスの古いバージョンを実行している)
Bozho

1
@Bozhoコレクションのみの遅延読み込みを指定しました。単純な文字列フィールドを遅延読み込みできますか?
vikiiii 2013

いいえ。列のサブセットを取得するには、クエリまたは別のマッピングされたエンティティを使用する必要があります
Bozho

上のそれのセットならば@Bozhoは、ちょっとあなたがして、これを答えてくださいすることができますfetchtype = LAZYゲッターhiberneteでコレクションを取得しようとするが、それは評価できない私に言って、エラーをスローした場合でも、デフォルト1
ВсеЕдно

16

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


11

私の知識によれば、どちらのタイプのフェッチも要件に依存します。

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

FetchType.EAGER 即時(つまり、要件が来る前に、不必要にレコードをフェッチしている)


11

デフォルトでは、すべてのコレクションおよびマップオブジェクトに対してフェッチルールがFetchType.LAZYあり、他のインスタンスに対してはFetchType.EAGERポリシーに従います。
簡単に言う@OneToManyと、@ManyToManyリレーションは関連オブジェクト(コレクションとマップ)を暗黙的にフェッチしませんが、検索操作はフィールド1 @OneToOne@ManyToOne1を介してカスケードされます。

(礼儀:-objectdbcom)


9

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

残念ながら、LAZYフェッチのデフォルトのフェッチプランのみをオーバーライドできます。EAGERフェッチは柔軟性が低く、多くのパフォーマンスの問題を引き起こす可能性があります

私のアドバイスは、フェッチはクエリ時の責任であるため、関連付けをイーガーにする衝動を抑えることです。したがって、すべてのクエリでfetchディレクティブを使用して、現在のビジネスケースに必要なものだけを取得する必要があります。


2
「EAGERフェッチは柔軟性が低く、多くのパフォーマンスの問題を引き起こす可能性があります。」...より真実のステートメントは、「EAGERフェッチを使用するか使用しないとパフォーマンスの問題が発生する可能性がある」です。遅延初期化されたフィールドへのアクセスにコストがかかり、使用頻度が低い特定のケースでは、遅延フェッチがパフォーマンスを向上させます。ただし、変数が頻繁に使用される場合、遅延初期化は、初期化よりもデータベースへのトリップを必要とするため実際にパフォーマンス低下させる可能性があります。独断的にではなく、FetchTypeを正しく適用することをお勧めします。
scottb

ここであなたの本を宣伝していますか?しかし、はい、それはユースケース、およびカーディナリティー関係で参照されるオブジェクトのサイズに依存すると感じます。
John Doe

6

Javadocから:

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

たとえば、熱心な人は怠惰な人より積極的です。レイジーは最初の使用時にのみ発生します(プロバイダーがヒントを取得する場合)。一方、熱心なものでは(先に)取得されます。


1
「最初の使用」とはどういう意味ですか?
レオン

@leon:イージーフィールドとレイジーフィールドを持つエンティティがあるとします。エンティティを取得すると、エンティティ参照を受け取るまでに、eagerフィールドがDBから読み込まれますが、lazyフィールドは読み込まれていない可能性があります。アクセサを介してフィールドにアクセスしようとした場合にのみフェッチされます。
TJクラウダー

@TJ Crowder、fetchtypeが定義されていない場合のデフォルトは何ですか?
Mahmoud Saleh

@MahmoudSaleh:わからない。それはおそらく何かに基づいて異なります。私は実際のプロジェクトでJPAを使用したことがないので、中身を理解していません。
TJクラウダー

2
@MahmoudS:デフォルトのフェッチタイプ:OneToMany:LAZY、ManyToOne:EAGER、ManyToMany:LAZY、OneToOne:EAGER、列:EAGER
Markus Pscheidt

5

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

FetchType.LAZY = getterメソッドを介して呼び出さない限り、これは関係をロードしません。

FetchType.EAGER =すべての関係が読み込まれます。

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

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

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

そうは言っても、状況応じて、これらの初期化のいずれかを使用できます。


1
「ゲッターメソッドを介して呼び出さない限り、関係をロードしない」という記述は重要であり、また、私の意見ではかなり遅れた設計決定です... アクセス時にフェッチすると想定した場合、ゲッター関数を明示的に呼び出さなかったため、そうではありませんでした。ところで、何が「ゲッター」機能を構成するのですか?JPA getMemberは、メンバーの名前パターンと完全に一致する呼び出された関数が呼び出されるまで、プロパティのロードを延期しますか?
ToVine 2018

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が遅延ロードされます。ただし、一方で、Books関連のコレクションsubjectのアソシエーションは、アノテーションが付けられて@ManyToOne、([default][1]for @ManyToOne、によってfetchType=EAGER)より早くロードされます。Subject.javaに@OneToManyfetchType.EAGERを配置するか、Books.java にfetchType.LAZYを配置することで、動作を変更できます@ManyToOne


1

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

ソース


1

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

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

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

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

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

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

1

こんにちは、私はあなたがこれを理解するのを助けるために2枚の写真を添付し​​ました。 ここに画像の説明を入力してください

ここに画像の説明を入力してください


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.