Я новичок в Java Persistence API и Hibernate.
В чем разница между FetchType.LAZY
и FetchType.EAGER
в Java Persistence API?
Я новичок в Java Persistence API и Hibernate.
В чем разница между FetchType.LAZY
и FetchType.EAGER
в Java Persistence API?
Ответы:
Иногда у вас есть две сущности, и между ними есть отношения. Например, у вас может быть названная сущность University
и другая сущность, Student
а в университете может быть много студентов:
Сущность университета может иметь некоторые базовые свойства, такие как идентификатор, имя, адрес и т. Д., А также свойство коллекции, называемое студенты, которое возвращает список студентов для данного университета:
public class University {
private String id;
private String name;
private String address;
private List<Student> students;
// setters and getters
}
Теперь, когда вы загружаете университет из базы данных, JPA загружает его поля id, name и address. Но у вас есть два варианта загрузки студентов:
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()
метод в первый раз, кэшируются ли результаты? чтобы в следующий раз я мог быстрее получить доступ к этим результатам?
EAGER
загрузка коллекций означает, что они извлекаются полностью во время извлечения их родителя. Так что, если у вас есть Course
и есть List<Student>
, все студенты выбираются из базы данных в то время, когда они Course
выбираются.
LAZY
с другой стороны, означает, что содержимое List
выбирается только при попытке доступа к ним. Например, по телефону course.getStudents().iterator()
. Вызов любого метода доступа для List
инициатора вызовет базу данных для получения элементов. Это реализуется путем создания прокси вокруг List
(или Set
). Так что для ваших ленивых коллекций конкретных типов нет ArrayList
и HashSet
, но PersistentSet
и PersistentList
(или PersistentBag
)
course.getStudents()
, он запускает SQL-запрос (видел это на консоли). В типе Lazy fetch тоже самое происходит. Так в чем же разница?
fetchtype = LAZY
умолчанию, даже если попытаться получить коллекцию с помощью getter, hibernete выдает ошибку, сообщающую, что не может оценить
Я могу рассмотреть производительность и использование памяти. Одно большое отличие состоит в том, что стратегия извлечения EAGER позволяет использовать извлеченный объект данных без сеанса. Почему?
Все данные извлекаются, когда стремятся отметить данные в объекте, когда сеанс подключен. Однако, в случае стратегии отложенной загрузки, отложенная загрузка помеченного объекта не извлекает данные, если сеанс отключен (после session.close()
оператора). Все это можно сделать с помощью Hibernate-прокси. Стремительная стратегия позволяет данным оставаться доступными после закрытия сессии.
По умолчанию для всех объектов коллекции и карты применяется правило выборки, FetchType.LAZY
а для других экземпляров оно соответствует FetchType.EAGER
политике.
Вкратце, @OneToMany
и @ManyToMany
отношения не извлекают связанные объекты (сбор и отображение) неявным образом, но операция поиска осуществляется каскадно через поле в @OneToOne
и @ManyToOne
единицах.
Оба FetchType.LAZY
и FetchType.EAGER
используются для определения плана выборки по умолчанию .
К сожалению, вы можете переопределить план выборки по умолчанию только для LAZY. Извлечение EAGER менее гибко и может привести ко многим проблемам с производительностью .
Мой совет заключается в том, чтобы ограничить желание сделать ваши ассоциации EAGER, потому что выборка - это вопрос времени. Поэтому все ваши запросы должны использовать директиву fetch, чтобы получить только то, что необходимо для текущего бизнес-кейса.
Из 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;
}
}
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();
}
}
}
Проверьте метод retrieve () файла Main.java. Когда мы получим Subject, его коллекция listBooks , снабженная комментариями @OneToMany
, будет загружаться лениво. Но, с другой стороны, связанное с Книгой объединение предмета коллекции , снабженное комментариями @ManyToOne
, загружается раньше ( [default][1]
для @ManyToOne
, fetchType=EAGER
). Мы можем изменить поведение, поместив fetchType.EAGER в @OneToMany
Subject.java или fetchType.LAZY @ManyToOne
в Books.java.
public enum FetchType extends java.lang.Enum Определяет стратегии для извлечения данных из базы данных. Стратегия EAGER - это требование времени выполнения провайдера постоянства, что данные должны извлекаться с нетерпением. Стратегия LAZY - это подсказка среде выполнения персистентного поставщика о том, что данные должны извлекаться лениво при первом обращении к ним. Реализация позволяет быстро получать данные, для которых указана подсказка стратегии LAZY. Пример: @Basic (fetch = LAZY) protected String getName () {возвращаемое имя; }
Я хочу добавить эту заметку к тому, что сказал «Кён Хван Мин» выше.
Предположим, вы используете 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;
}
//...
}
LAZY: он лениво выбирает дочерние сущности, т.е. во время выборки родительской сущности, он просто выбирает прокси (созданный cglib или любой другой утилитой) дочерних сущностей, и когда вы получаете доступ к любому свойству дочерней сущности, он фактически извлекается hibernate.
EAGER: он выбирает дочерние объекты вместе с родительскими.
Для лучшего понимания перейдите к документации Jboss или вы можете использовать hibernate.show_sql=true
для своего приложения и проверить запросы, выданные hibernate.