私はJava Persistence APIとHibernateの初心者です。
Java Persistence API FetchType.LAZY
との違いは何FetchType.EAGER
ですか?
私はJava Persistence APIとHibernateの初心者です。
Java Persistence API FetchType.LAZY
との違いは何FetchType.EAGER
ですか?
回答:
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つのオプションがあります。
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.
}
getStudents()
がありますが、このメソッドの実行までに、これができない場合があります。が呼び出され、セッションはすでに閉じられており、エンティティは切り離されています。同様に、クライアント/サーバーアーキテクチャ(例:Swingクライアント/ JEEサーバー)があり、エンティティ/ DTOがネットワーク経由でクライアントに転送され、これらのシナリオでは、エンティティの方法が原因でレイジーロードが機能しないことがよくあります。ワイヤーを介してシリアル化されます。
getStudents()
メソッドを初めて呼び出すと、結果はキャッシュされますか?次回はこれらの結果により速くアクセスできるようにするには?
基本的に、
LAZY = fetch when needed
EAGER = fetch immediately
EAGER
コレクションのロードとは、親がフェッチされるときにコレクションが完全にフェッチされることを意味します。あなたが持っているのであればCourse
、それは持っている List<Student>
、すべての学生がフェッチされたデータベースから、一度にCourse
フェッチされます。
LAZY
一方、コンテンツList
にアクセスしようとした場合にのみ、コンテンツがフェッチされることを意味します。たとえば、を呼び出しcourse.getStudents().iterator()
ます。でアクセスメソッドをList
呼び出すと、データベースを呼び出して要素を取得します。これは、List
(またはSet
)の周囲にプロキシを作成することで実装されます。したがって、遅延コレクションの具体的な型はArrayList
and HashSet
ではなくPersistentSet
、and PersistentList
(またはPersistentBag
)です。
course.getStudents()
、それはSQLクエリを起動します(コンソールで見ました)。レイジーフェッチタイプでも同じことが起こります。それで、違いは何ですか?
fetchtype = LAZY
ゲッターhiberneteでコレクションを取得しようとするが、それは評価できない私に言って、エラーをスローした場合でも、デフォルト1
パフォーマンスとメモリ使用率を検討します。1つの大きな違いは、EAGERフェッチ戦略では、セッションなしでフェッチされたデータオブジェクトを使用できることです。どうして?
セッションが接続されたときにオブジェクト内の熱心なマークデータが取得されると、すべてのデータがフェッチされます。ただし、遅延ロード戦略の場合、セッションが切断されていると(session.close()
ステートメントの後)、マークされたオブジェクトの遅延ロードはデータを取得しません。hibernateプロキシで作成できるすべてのこと。熱心な戦略により、セッション終了後もデータを引き続き利用できます。
デフォルトでは、すべてのコレクションおよびマップオブジェクトに対してフェッチルールがFetchType.LAZY
あり、他のインスタンスに対してはFetchType.EAGER
ポリシーに従います。
簡単に言う@OneToMany
と、@ManyToMany
リレーションは関連オブジェクト(コレクションとマップ)を暗黙的にフェッチしませんが、検索操作はフィールド1 @OneToOne
と@ManyToOne
1を介してカスケードされます。
FetchType.LAZY
とFetchType.EAGER
は両方とも、デフォルトのフェッチプランを定義するために使用されます。
残念ながら、LAZYフェッチのデフォルトのフェッチプランのみをオーバーライドできます。EAGERフェッチは柔軟性が低く、多くのパフォーマンスの問題を引き起こす可能性があります。
私のアドバイスは、フェッチはクエリ時の責任であるため、関連付けをイーガーにする衝動を抑えることです。したがって、すべてのクエリでfetchディレクティブを使用して、現在のビジネスケースに必要なものだけを取得する必要があります。
Javadocから:
EAGER戦略は、データを熱心にフェッチする必要がある永続性プロバイダーランタイムの要件です。LAZY戦略は、データが最初にアクセスされたときにデータを遅延フェッチする必要があるという永続性プロバイダーランタイムへのヒントです。
たとえば、熱心な人は怠惰な人より積極的です。レイジーは最初の使用時にのみ発生します(プロバイダーがヒントを取得する場合)。一方、熱心なものでは(先に)取得されます。
Lazy
フェッチ・タイプは、明示的にマークしない限り、Hibernateが選択され、デフォルトであるEager
タイプを取得します。より正確かつ簡潔にするために、違いは以下のように述べることができます。
FetchType.LAZY
= getterメソッドを介して呼び出さない限り、これは関係をロードしません。
FetchType.EAGER
=すべての関係が読み込まれます。
これら2つのフェッチタイプの長所と短所。
Lazy initialization
不要な計算を回避してパフォーマンスを向上させ、メモリ要件を削減します。
Eager initialization
より多くのメモリを消費し、処理速度が遅くなります。
そうは言っても、状況に応じて、これらの初期化のいずれかを使用できます。
getMember
は、メンバーの名前パターンと完全に一致する呼び出された関数が呼び出されるまで、プロパティのロードを延期しますか?
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に@OneToMany
fetchType.EAGERを配置するか、Books.java にfetchType.LAZYを配置することで、動作を変更できます@ManyToOne
。
public enum FetchType extends java.lang.Enumデータベースからデータをフェッチするための戦略を定義します。EAGER戦略は、データを熱心にフェッチする必要がある永続性プロバイダーランタイムの要件です。LAZY戦略は、データが最初にアクセスされたときにデータを遅延フェッチする必要があるという永続性プロバイダーランタイムへのヒントです。実装は、LAZY戦略のヒントが指定されたデータを積極的にフェッチすることが許可されています。例:@Basic(fetch = LAZY)protected String getName(){return name; }
このメモを上記の「キョンファンミン」が言ったことに付け加えたい。
この単純なアーキテクトでSpring Restを使用しているとします。
コントローラ<->サービス<->リポジトリ
また、一部のデータをフロントエンドに返したい場合、を使用FetchType.LAZY
している場合は、セッションがサービスで閉じられているためJSON Mapper Object
データを取得できないため、コントローラーメソッドにデータを返した後に例外が発生します。
この問題を解決するための3つの一般的なオプションがあり、設計、パフォーマンス、および開発者によって異なります。
FetchType.EAGER
。これにより、セッションはコントローラーメソッドで存続します。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
、アプリでを使用して、休止状態によって発行されたクエリを確認してください。