私はJava Persistence APIとHibernateの初心者です。
Java Persistence APIの FetchType.LAZY
と FetchType.EAGER
の違いは何ですか?
時にはあなたは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()メソッドを呼び出すときにオンデマンドで(つまり遅延的に)ロードすることです。
大学に多くの学生がいるとき、彼らが必要とされていないときにそれをすべての学生にロードすることは効率的ではありません。したがって、そのような場合には、実際に必要なときに生徒に負荷をかけることを宣言できます。これは遅延ロードと呼ばれます。
基本的に、
LAZY = fetch when needed
EAGER = fetch immediately
コレクションのEAGER
ロードは、それらの親がフェッチされる時点でそれらが完全にフェッチされることを意味します。あなたがCourse
を持っていて、それがList<Student>
を持っている場合、すべての生徒は データベースから Course
がフェッチされた時点でフェッチされます。
一方、LAZY
は、List
の内容がアクセスしようとしたときにのみ取得されることを意味します。たとえば、course.getStudents().iterator()
を呼び出すことによって。 List
で任意のアクセス方法を呼び出すと、要素を取得するためにデータベースへの呼び出しが開始されます。これはList
(またはSet
)の周囲にProxyを作成することによって実装されます。だからあなたの怠惰なコレクションのために、具象型はArrayList
とHashSet
ではなく、PersistentSet
とPersistentList
(またはPersistentBag
)です。
パフォーマンスとメモリ使用率を検討します。大きな違いの1つは、EAGERフェッチ戦略によって、セッションなしでフェッチされたデータオブジェクトを使用できることです。どうして?
セッションが接続されているときにオブジェクト内のマーク付きデータを積極的に取得すると、すべてのデータが取得されます。ただし、遅延ローディング戦略の場合、セッションが切断されていると(session.close()
ステートメントの後)、遅延ローディングされたオブジェクトはデータを取得しません。すべて休止状態のプロキシによって行うことができます。積極的な戦略により、セッション終了後もデータを利用できるようになります。
デフォルトでは、すべてのコレクションオブジェクトとマップオブジェクトでフェッチルールはFetchType.LAZY
であり、それ以外のインスタンスではFetchType.EAGER
ポリシーに従います。
簡単に言うと、@OneToMany
と@ManyToMany
の関係は暗黙のうちに関連するオブジェクト(コレクションとマップ)を取得するわけではありませんが、検索操作は@OneToOne
と@ManyToOne
の中のフィールドを通してカスケードされます。
私の知る限りでは、両方のタイプのフェッチはあなたの要求次第です。
FetchType.LAZY
はオンデマンドです(つまり、データが必要な場合)。
FetchType.EAGER
は即時です(つまり、要求が来る前に不必要にレコードを取得しています)
FetchType.LAZY
とFetchType.EAGER
は両方とも デフォルトのフェッチプラン を定義するために使用されます。
残念ながら、あなたはLAZYフェッチのためのデフォルトのフェッチプランしか上書きできません。 EAGERフェッチは柔軟性が低く、 多くのパフォーマンス問題を引き起こす可能性があります 。
フェッチは問い合わせ時の責任であるため、私のアドバイスはあなたのアソシエーションをEAGERにすることの衝動を抑えることです。そのため、すべてのクエリで、 fetch ディレクティブを使用して、現在のビジネスケースに必要なものだけを取得するようにしてください。
Javadoc :から
EAGER戦略は、データを積極的に取得する必要があることを永続プロバイダーランタイムに課す要件です。 LAZY戦略は、データが最初にアクセスされたときに遅延してフェッチされるべきであるという永続プロバイダーランタイムへのヒントです。
例えば、熱心な人は怠け者よりも積極的です。 (プロバイダがヒントを得た場合)レイジーは初回使用時にのみ発生しますが、熱心なものではプリフェッチされる場合があります。
明示的にLazy
フェッチタイプを指定しない限り、Eager
フェッチタイプはデフォルトでHibernateによって選択されます。より正確で簡潔にするために、違いは以下のように述べることができます。
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のretrieve()メソッドを確認してください。 Subjectを取得すると、そのコレクション listBooks (@OneToMany
のアノテーションが付いている)が遅延ロードされます。しかし、その一方で、@ManyToOne
でアノテーションが付けられたBooks関連のcollection subject の関連付けは、([default][1]
に対して@ManyToOne
、fetchType=EAGER
によって)早くロードされます。 Books.Javaでは、fetchType.EAGERを@OneToMany
Subject.Javaに、またはfetchType.LAZYを@ManyToOne
に配置することで、動作を変更できます。
public enum FetchTypeはJava.lang.Enumを拡張したものです。データベースからデータを取得するための戦略を定義します。 EAGER戦略は、データを積極的に取得する必要があることを永続プロバイダーランタイムに課す要件です。 LAZY戦略は、データが最初にアクセスされたときに遅延してフェッチされるべきであるという永続プロバイダーランタイムへのヒントです。実装は、LAZY戦略ヒントが指定されているデータを熱心に取得することを許可されています。例:@Basic(fetch = LAZY)protected String getName(){戻り名; }
上記の「キョンファンミン」が言ったことにこのメモを加えたい。
この単純なアーキテクトでSpring Restを使用しているとします:
コントローラ<->サービス<->リポジトリ
また、FetchType.LAZY
を使用している場合、一部のデータをフロントエンドに返したい場合、サービスでセッションが閉じられるため、コントローラーメソッドにデータを返した後、JSON Mapper Object
データを取得できません。
この問題を解決するための3つの一般的なオプションがあり、設計、パフォーマンス、および開発者によって異なります。
FetchType.EAGER
を使用することです。これにより、セッションはコントローラーメソッドで引き続き有効になります。FetchType.LAZY
をコンバーターメソッドで使用してEntity
から別のデータオブジェクトDTO
にデータを転送し、コントローラーに送信することです。したがって、セッションが閉じられても例外はありません。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;
}
//...
}
LAZY:子エンティティを遅延的に取得します。つまり、親エンティティの取得時には、子エンティティのプロキシ(cglibまたは他のユーティリティによって作成されたもの)を取得するだけです。
EAGER:それは親と共に子エンティティを取得します。
よりよく理解するためにJbossのドキュメントを参照するか、あなたのアプリケーションにhibernate.show_sql=true
を使用してHibernateによって発行されたクエリをチェックすることができます。