Java Persistence APIのFetchType怠zyと熱心の違いは?
-
24-10-2019 - |
質問
私はJava Persistence APIとHibernateの初心者です。
違いは何ですか FetchType.LAZY
と FetchType.EAGER
Java Persistence APIで?
解決
時には2つのエンティティがあり、それらの間に関係があります。たとえば、呼ばれるエンティティがある場合があります University
と呼ばれる別のエンティティ Student
そして、大学には多くの学生がいるかもしれません:
大学のエンティティには、ID、名前、住所などの基本的なプロパティと、特定の大学の学生のリストを返す学生と呼ばれる収集プロパティがある場合があります。
public class University {
private String id;
private String name;
private String address;
private List<Student> students;
// setters and getters
}
これで、データベースから大学をロードすると、JPAはID、名前、およびアドレスフィールドをロードします。ただし、生徒のロード方法については、次の2つのオプションがあります。
- 残りのフィールドと一緒にロードするには(つまり、熱心に)、または
- あなたが大学に電話するときにそれをオンデマンドでロードする(すなわち怠け者)
getStudents()
方法。
大学に多くの学生がいる場合、特に必要でない場合は、すべての学生を一緒にロードすることは効率的ではなく、そのような場合には、実際に必要なときに生徒を積み込んでもらいたいと宣言できます。これは怠zyなロードと呼ばれます。
これが例です 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
怠lazにロードされるように明示的にマークされています:
@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
)。したがって、あなたの怠zyなコレクションのために、コンクリートタイプはそうではありません ArrayList
と HashSet
, 、 しかし PersistentSet
と PersistentList
(また PersistentBag
)
パフォーマンスとメモリの利用を検討する場合があります。大きな違いの1つは、熱心なフェッチ戦略では、セッションなしでフェッチされたデータオブジェクトを使用できることです。なんで?
セッションが接続されているときに、オブジェクト内の熱心なマークデータを使用すると、すべてのデータが取得されます。ただし、怠zyな読み込み戦略の場合、セッションが切断された場合(後にデータを取得しません(後にデータを取得しません) session.close()
声明)。 Hibernate Proxyによって作成できます。熱心な戦略により、セッションを閉じる後もデータを利用できるようになります。
デフォルトでは、すべてのコレクションとマップオブジェクトについて、フェッチングルールは FetchType.LAZY
そして、他のインスタンスについては、それは続きます FetchType.EAGER
ポリシー。
簡単に言えば、 @OneToMany
と @ManyToMany
関係は関連するオブジェクト(コレクションとマップ)を暗黙のうちに取得しませんが、検索操作はフィールドを通じてカスケードされます @OneToOne
と @ManyToOne
ワンズ。
私の知る限り、両方のタイプのフェッチはあなたの要件に依存します。
FetchType.LAZY
オンデマンドです(つまり、データが必要な場合)。
FetchType.EAGER
即時です(つまり、私たちの要件が来る前に、私たちは不必要に記録を取得しています)
両方 FetchType.LAZY
と FetchType.EAGER
定義するために使用されます デフォルトのフェッチプラン.
残念ながら、怠zyなフェッチのためのデフォルトのフェッチ計画をオーバーライドすることしかできません。熱心なフェッチングは柔軟性が低く、できます 多くのパフォーマンスの問題につながります.
私のアドバイスは、フェッチはクエリタイムの責任であるため、あなたの協会を熱心にする衝動を抑えることです。したがって、すべてのクエリはを使用する必要があります フェッチ 現在のビジネスケースに必要なもののみを取得するための指令。
から Javadoc:
熱心な戦略は、データを熱心に取得する必要があるという永続プロバイダーのランタイムの要件です。怠zyな戦略は、データが最初にアクセスされたときにゆっくりとフェッチする必要があるという永続プロバイダーのランタイムのヒントです。
たとえば、熱心は怠zyよりも積極的です。怠zyは最初の使用でのみ発生します(プロバイダーがヒントをとる場合)、一方、熱心なもの(5月)が事前に獲得されます。
Lazy
Fetch Typeは、明示的にマークしない限り、デフォルトでHibernateによって選択されます Eager
フェッチタイプ。より正確で簡潔にするために、違いを以下に記載することができます。
FetchType.LAZY
= Getterメソッドを介して呼び出しない限り、これは関係をロードしません。
FetchType.EAGER
=これにより、すべての関係がロードされます。
これら2つのフェッチタイプの長所と短所。
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の取得()メソッドを確認してください。主題を得ると、そのコレクション リストブック, 、で注釈が付けられています @OneToMany
, 、怠lazにロードされます。しかし、一方で、コレクションの本関連協会 主題, 、で注釈が付けられています @ManyToOne
, 、Ealgerly(by [default][1]
為に @ManyToOne
, fetchType=EAGER
)。 fetchtype.eegerを配置することで動作を変更できます @OneToMany
subject.javaまたはfetchtype.lazy on @ManyToOne
books.javaで。
public enum fetchType拡張java.lang.enumは、データベースからデータを取得するための戦略を定義します。熱心な戦略は、データを熱心に取得する必要があるという永続プロバイダーのランタイムの要件です。怠zyな戦略は、データが最初にアクセスされたときにゆっくりとフェッチする必要があるという永続プロバイダーのランタイムのヒントです。この実装は、怠zyな戦略ヒントが指定されているデータを熱心に取得することが許可されています。例:@basic(fetch = lazy)保護された文字列getname(){return name; }
このメモを「Kyung Hwan Min」が上記で言ったことに追加したいと思います。
このシンプルなアーキテクトで春の休憩を使用しているとします。
コントローラー<->サービス<-->リポジトリ
そして、あなたが使用している場合、あなたはいくつかのデータをフロントエンドに返したいです 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;
}
//...
}
怠zy:それは、子のエンティティを取得する時点で、子どもエンティティを怠lazにフェッチします。子エンティティのプロキシ(cglibまたはその他のユーティリティによって作成された)を取得するだけで、子エンティティの財産にアクセスすると、実際にハイバーネートによってフェッチされます。
熱心:それは親とともに子供のエンティティを取得します。
よりよく理解するためにJBossのドキュメントにアクセスするか、使用できます hibernate.show_sql=true
アプリの場合は、Hibernateが発行したクエリを確認してください。