Разница между FetchType LAZY и EAGER в Java Persistence API?

Я новичок в Java Persistence API и Hibernate.

В чем разница между FetchType.LAZY и FetchType.EAGER в Java Persistence API?

Ответ 1

Иногда у вас есть две сущности, и между ними есть отношения. Например, у вас может быть сущность с именем University и другая сущность с именем Student, а в университете может быть много студентов:

У сущности университета могут быть некоторые базовые свойства, такие как идентификатор, имя, адрес и т.д., А также свойство коллекции, называемое студенты, которое возвращает список студентов для данного университета:

A university has many students

public class University {
   private String id;
   private String name;
   private String address;
   private List<Student> students;

   // setters and getters
}

Теперь, когда вы загружаете университет из базы данных, JPA загружает его поля id, name и address. Но у вас есть два варианта загрузки студентов:

  1. Чтобы загрузить его вместе с остальными полями (т.е. с нетерпением), или
  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.
}

Ответ 2

В принципе,

LAZY = fetch when needed
EAGER = fetch immediately

Ответ 3

EAGER Загрузка коллекций означает, что они извлекаются полностью во время получения родительских прав. Поэтому, если у вас есть Course, и он имеет List<Student>, все ученики извлекаются из базы данных во время получения Course.

LAZY, с другой стороны, означает, что содержимое List извлекается только при попытке получить к ним доступ. Например, вызывая course.getStudents().iterator(). Вызов любого метода доступа в List приведет к вызову базы данных для извлечения элементов. Это реализовано созданием прокси-сервера вокруг List (или Set). Поэтому для ваших ленивых коллекций конкретные типы не ArrayList и HashSet, но PersistentSet и PersistentList (или PersistentBag)

Ответ 4

Я могу рассмотреть производительность и использование памяти. Одно большое различие заключается в том, что стратегия EAGER fetch позволяет использовать извлеченный объект данных без сеанса. Почему?
Все данные извлекаются, когда требуются помеченные данные в объекте, когда сеанс подключен. Однако в случае ленивой стратегии загрузки ленивая загрузка отмеченного объекта не извлекает данные, если сеанс отключен (после оператора session.close()). Все, что может быть сделано с помощью hibernate proxy. Стремительная стратегия позволяет сохранять данные после закрытия сессии.

Ответ 5

По умолчанию для всех объектов коллекции и карты правило выборки FetchType.LAZY, а для других экземпляров - политика FetchType.EAGER.
Короче говоря, отношения @OneToMany и @ManyToMany не выводят связанные объекты (сбор и карту) неявно, но операция поиска каскадируется через поле в @OneToOne и @ManyToOne.

(любезность: - objectdbcom)

Ответ 6

Как мне известно, оба типа выборки зависят от вашего требования.

FetchType.LAZY предоставляется по требованию (т.е. когда нам нужны данные).

FetchType.EAGER является непосредственным (т.е. перед тем, как наше требование приходит, мы ненужно извлекаем запись)

Ответ 7

Оба FetchType.LAZY и FetchType.EAGER используются для определения плана выборки по умолчанию.

К сожалению, вы можете только переопределить план выборки по умолчанию для получения LAZY. Выбор EAGER менее гибок и может привести к многочисленным проблемам производительности.

Мой совет заключается в том, чтобы ограничить желание создавать ассоциации EAGER, потому что выборка - это ответственность за время запроса. Таким образом, все ваши запросы должны использовать директиву fetch, чтобы получить только то, что необходимо для текущего бизнес-кейса.

Ответ 8

Из Javadoc:

Стратегия EAGER является обязательным требованием для среды выполнения поставщика непрерывности, которую данные должны получать с нетерпением. Стратегия LAZY - это намек на время выполнения провайдера настойчивости, при котором данные должны извлекаться лениво при первом доступе.

Например, нетерпеливый более активен, чем ленивый. Lazy происходит только при первом использовании (если провайдер принимает подсказку), тогда как с нетерпеливыми вещами (может) получить предварительную выборку.

Ответ 9

Тип Lazy Fetch по умолчанию выбран Hibernate, если вы явно не отметили Eager Тип Fetch. Чтобы быть более точным и кратким, разница может быть указана ниже.

FetchType.LAZY= Это не загружает отношения, если вы не вызываете его с помощью метода getter.

FetchType.EAGER= Здесь загружаются все отношения.

Плюсы и минусы этих двух типов выборки.

Lazy initialization повышает производительность, избегая ненужных вычислений и уменьшая требования к памяти.

Eager initialization потребляет больше памяти, а скорость обработки медленная.

Сказав, что зависит от ситуации, можно использовать одну из этих инициализаций.

Ответ 10

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.

Ответ 11

public enum FetchType расширяет java.lang.Enum Определяет стратегии для извлечения данных из базы данных. Стратегия EAGER является обязательным требованием для среды выполнения поставщика непрерывности, которую данные должны получать с нетерпением. Стратегия LAZY - это намек на время выполнения провайдера настойчивости, при котором данные должны извлекаться лениво при первом доступе. Реализации разрешено охотно извлекать данные, для которых указана стратегия LAZY.    Пример:    @Basic (выборка = ЛЕНИВЫЙ)    protected String getName() {return name; }

Источник

Ответ 12

Я хочу добавить эту заметку к тому, что сказал "Кён Хван Мин" выше.

Предположим, вы используете Spring Rest с этим простым архитектором:

Контроллер <-> Сервис <-> Репозиторий

И вы хотите вернуть некоторые данные во FetchType.LAZY интерфейс, если вы используете FetchType.LAZY, вы получите исключение после того, как FetchType.LAZY данные в метод контроллера, так как сеанс закрыт в Сервисе, поэтому JSON Mapper Object не может получить данные.

Существует три распространенных варианта решения этой проблемы, в зависимости от дизайна, производительности и разработчика:

  1. Самый простой - использовать FetchType.EAGER, чтобы сеанс все еще оставался живым при методе контроллера.
  2. Решения по борьбе с шаблонами, позволяющие запустить сеанс до окончания его выполнения, создают огромную проблему с производительностью в системе.
  3. FetchType.LAZY использовать FetchType.LAZY с методом преобразователя для передачи данных из Entity в другой объект данных DTO и отправки их в контроллер, поэтому исключений нет, если сеанс закрыт.

Ответ 13

@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;
    }
    //...
}

Ответ 14

LAZY: он извлекает дочерние объекты лениво, т.е. во время получения родительского объекта он просто извлекает прокси (созданный cglib или любой другой утилитой) дочерних объектов и когда вы обращаетесь к любому свойству дочернего объекта, тогда он фактически извлекается спящий режим.

EAGER: он извлекает дочерние объекты вместе с родительским.

Для лучшего понимания обратитесь к документации Jboss или вы можете использовать hibernate.show_sql=true для своего приложения и проверить запросы, выданные спящим режимом.