Я новачок у програмі Java Persistence API і в режимі глибокого сну.
У чому різниця між FetchType.LAZY
і FetchType.EAGER
в Java Persistence API?
Я новачок у програмі Java Persistence API і в режимі глибокого сну.
У чому різниця між FetchType.LAZY
і FetchType.EAGER
в Java Persistence API?
Відповіді:
Іноді у вас є дві сутності, і між ними існують відносини. Наприклад, у вас може бути названа організація, University
а інша названа, Student
а в університеті може бути багато студентів:
Суб'єкт університету може мати деякі основні властивості, такі як id, ім'я, адреса тощо, а також властивість колекції під назвою студентів, яка повертає список студентів для даного університету:
public class University {
private String id;
private String name;
private String address;
private List<Student> students;
// setters and getters
}
Тепер, коли ви завантажуєте університет із бази даних, JPA завантажує для вас поля ідентифікатора, імені та адреси. Але у вас є два варіанти завантаження студентів:
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 client / JEE-сервер), і сутності / DTO передаються клієнту по дроту, і знову ж таки найчастіше в цих сценаріях ледаче завантаження не працюватиме через те, як сутності серіалізуються по дроту.
getStudents()
вперше викликаю метод, кешуються результати? щоб я міг отримати доступ до цих результатів швидше наступного разу?
EAGER
завантаження колекцій означає, що вони отримуються повністю в момент отримання батьком. Отже, якщо у вас є Course
і у неї є List<Student>
, всі студенти виймаються з бази даних в момент її отримання Course
.
LAZY
з іншого боку, означає, що вміст List
файлу дістається лише тоді, коли ви намагаєтесь отримати доступ до них. Наприклад, зателефонувавши course.getStudents().iterator()
. Виклик будь-якого методу доступу за List
заповітом ініціює виклик до бази даних для отримання елементів. Це реалізується шляхом створення проксі-сервера навколо List
(або Set
). Так що для ваших ледачих колекцій, типи бетону не ArrayList
та HashSet
, але PersistentSet
і PersistentList
(або PersistentBag
)
course.getStudents()
, він запускає SQL-запит (бачив, що на консолі). У типу "Ледачий вибор" також відбувається те ж саме. Отже, в чому різниця ??
fetchtype = LAZY
замовчуванням, навіть якщо спробувати отримати колекцію з Getter hibernete кидає помилку, кажучи мені, що не може оцінити
Я можу врахувати продуктивність та використання пам'яті. Одна велика відмінність полягає в тому, що стратегія отримання EAGER дозволяє використовувати об'єкт даних без сеансу. Чому?
Усі дані дістаються, коли охоче позначені дані в об'єкті під час підключення сеансу. Однак, у випадку стратегії ледачого завантаження, лінивий завантажений позначений об'єкт не отримує дані, якщо сеанс відключений (після session.close()
заяви). Все, що може зробити сплячий проксі. Страстна стратегія дозволяє залишатись доступними після закриття сесії.
За замовчуванням для всіх об’єктів колекції та картографування дістається правило вилучення, FetchType.LAZY
а для інших випадків воно дотримується FetchType.EAGER
політики.
Коротше кажучи, @OneToMany
і @ManyToMany
відносини не отримують пов'язаних об'єктів (колекція та карта) неявно, але операція пошуку каскадується через поле в @OneToOne
і@ManyToOne
.
Обидва FetchType.LAZY
і FetchType.EAGER
використовуються для визначення плану вибору за замовчуванням .
На жаль, ви можете змінити план вибору за замовчуванням для вилучення LAZY. Вибір EAGER є менш гнучким і може призвести до багатьох проблем з продуктивністю .
Моя порада - стримати прагнення зробити ваші асоціації EAGER, оскільки отримання даних - це відповідальність за час запитів. Тому всі ваші запити повинні використовувати вибірку директиви тільки отримати те , що необхідно для поточного бізнес - кейса.
Від Javadoc :
Стратегія EAGER - це вимога, що стосується часу наполегливого виконання постачальника, щоб дані повинні бути охоче отримані. Стратегія LAZY - це натяк на тривалість роботи постачальника наполегливості, що дані слід отримувати ліниво під час першого доступу.
Наприклад, прагнення є більш ініціативним, ніж ледачий. Ледачий трапляється лише при першому використанні (якщо постачальник підкаже), тоді як з нетерплячими речами (можливо) заздалегідь вибирайте.
Тип Lazy
Fetch за замовчуванням вибирається Hibernate, якщо явно не позначеноEager
тип Fetch. Щоб бути більш точним і стислим, різницю можна вказати нижче.
FetchType.LAZY
= Це не завантажує відносини, якщо ви не викликаєте їх методом getter.
FetchType.EAGER
= Це завантажує всі відносини.
Плюси і мінуси цих двох типів отримання.
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;
}
}
Тема.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();
}
}
}
Перевірте метод retrieve () Main.java. Коли ми отримаємо Subject, то його колекції списку колекцій , анотовані з ними @OneToMany
, будуть ліниво завантажуватися. Але, з іншого боку, Книги, пов’язані з асоціацією тематики колекції , з анотацією @ManyToOne
, завантажуються ретельно (від [default][1]
за @ManyToOne
, fetchType=EAGER
). Ми можемо змінити поведінку, розмістивши fetchType.EAGER на @OneToMany
Subject.java або fetchType.LAZY на @ManyToOne
Books.java.
public enum FetchType розширює java.lang.Enum Визначає стратегії для отримання даних із бази даних. Стратегія EAGER - це вимога, що стосується часу наполегливого виконання постачальника, щоб дані повинні бути охоче отримані. Стратегія LAZY - це натяк на тривалість роботи постачальника наполегливості, що дані слід отримувати ліниво під час першого доступу. Реалізація дозволена з нетерпінням отримувати дані, для яких було вказано підказку стратегії LAZY. Приклад: @Basic (fetch = LAZY) захищає String getName () {return name; }
Я хочу додати цю замітку до того, що "Кюн Хван Мін" сказав вище.
Припустимо, ви використовуєте Spring Rest з цим простим архітектором:
Контролер <-> Сервіс <-> сховище
І ви хочете повернути деякі дані на передній план, якщо ви використовуєте FetchType.LAZY
, ви отримаєте виняток після повернення даних до методу контролера, оскільки сесія закрита в Сервісі, томуJSON Mapper Object
не зможуть отримати.
Існує три загальних варіанти вирішення цієї проблеми, залежить від дизайну, продуктивності та розробника:
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;
}
//...
}
ЛЕЗИ: Він отримує дочірні сутності ліниво, тобто під час отримання материнської сутності він просто отримує проксі (створений cglib або будь-якою іншою утилітою) дочірніх сутностей, і коли ви отримуєте доступ до будь-якого майна дочірньої сутності, то він фактично дістається в сплячому режимі.
EAGER: він отримує дочірні сутності разом з батьками.
Для кращого розуміння перейдіть до документації Jboss або ви можете використовувати hibernate.show_sql=true
для свого додатка та перевірити запити, видані в сплячку.