¿Diferencia entre FetchType LAZY y EAGER en Java Persistence API?


554

Soy un novato en Java Persistence API e Hibernate.

¿Cuál es la diferencia entre FetchType.LAZYy FetchType.EAGERen Java Persistence API?


1
La carga de colecciones de EAGER significa que se recuperan completamente en el momento en que se recuperan sus padres. Mientras se carga EAGER, se busca a todo mi hijo. El elemento secundario se obtiene en PersistentSet y PersistentList (o PersistentBag), dentro de Persistent Bag, se muestra como una Lista de matrices. ¿Es correcto? ..
Geetha

Respuestas:


1066

A veces tienes dos entidades y hay una relación entre ellas. Por ejemplo, puede tener una entidad llamada Universityy otra entidad llamadaStudent y una universidad puede tener muchos estudiantes:

La entidad universitaria puede tener algunas propiedades básicas, como id, nombre, dirección, etc., así como una propiedad de colección llamada estudiantes que devuelve la lista de estudiantes de una universidad determinada:

Una universidad tiene muchos estudiantes

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

   // setters and getters
}

Ahora, cuando carga una universidad desde la base de datos, JPA carga sus campos de identificación, nombre y dirección por usted. Pero tiene dos opciones sobre cómo deben cargarse los estudiantes:

  1. Para cargarlo junto con el resto de los campos (es decir, con entusiasmo), o
  2. Para cargarlo a pedido (es decir, perezosamente) cuando llama al getStudents()método de la universidad .

Cuando una universidad tiene muchos estudiantes, no es eficiente cargar a todos sus estudiantes junto con ella, especialmente cuando no son necesarios y, en tales casos, puede declarar que desea que los estudiantes se carguen cuando realmente se necesitan. Esto se llama carga diferida.

Aquí hay un ejemplo, donde studentsestá marcado explícitamente para cargarse con entusiasmo:

@Entity
public class University {

    @Id
    private String id;

    private String name;

    private String address;

    @OneToMany(fetch = FetchType.EAGER)
    private List<Student> students;

    // etc.    
}

Y aquí hay un ejemplo donde studentsestá marcado explícitamente para ser cargado perezosamente:

@Entity
public class University {

    @Id
    private String id;

    private String name;

    private String address;

    @OneToMany(fetch = FetchType.LAZY)
    private List<Student> students;

    // etc.
}

55
@BehrangSaeedzadeh puede enumerar algunas diferencias prácticas o las ventajas y desventajas de cada tipo de carga (además de la eficiencia que mencionó). ¿Por qué querría uno usar carga ansiosa?
ADTC

73
@ADTC Para que funcione la carga diferida, la sesión JDBC aún debe estar abierta cuando las entidades de destino quieran cargarse en la memoria invocando el método getter (p getStudents(). Ej. ), Pero a veces esto no es posible, porque para cuando este método se llama, la sesión ya está cerrada y la entidad separada. Del mismo modo, a veces tenemos una arquitectura cliente / servidor (por ejemplo, cliente Swing / servidor JEE) y las entidades / DTO se transfieren a través del cable al cliente y nuevamente en estos escenarios la carga diferida no funcionará debido a la forma en que las entidades se serializan a través del cable.
TheFooProgrammer

44
Me gustaría agregar más información a esta respuesta de mi libro: para ahorrar memoria, la carga diferida generalmente se usa para una o varias relaciones. Para uno a uno, generalmente se usa Eager.
Erran Morad

2
En la carga diferida, cuando llamo al getStudents()método por primera vez, ¿se guardan en caché los resultados? para poder acceder a esos resultados más rápido la próxima vez?
JavaTechnical

2
@JavaTechnical depende si habilita el caché de segundo nivel (habilitado por defecto)
Ced

285

Básicamente,

LAZY = fetch when needed
EAGER = fetch immediately

11
Muy claro, pero solo después de leer la respuesta de @ Behang. Gracias por un resumen claro. :-)
Nabin

66

EAGERla carga de colecciones significa que se recuperan completamente en el momento en que se recupera su padre. Entonces, si tiene Coursey tiene List<Student>, todos los estudiantes se obtienen de la base de datos en el momento en que Coursese obtiene.

LAZYpor otro lado, significa que los contenidos de los Listarchivos solo se obtienen cuando intenta acceder a ellos. Por ejemplo, llamando course.getStudents().iterator(). Llamar a cualquier método de acceso en el Listiniciará una llamada a la base de datos para recuperar los elementos. Esto se implementa creando un Proxy alrededor de List(o Set). Entonces, para sus colecciones perezosas, los tipos concretos no son ArrayListy HashSet, sino PersistentSety PersistentList(o PersistentBag)


Utilicé ese concepto para obtener los detalles de una entidad secundaria, pero no puedo ver ninguna diferencia entre ellos. Cuando especifico Eager fetch, recupera todo y cuando lo depuro, veo "Bean diferido" en la entidad secundaria. Cuando digo course.getStudents(), dispara una consulta SQL (lo vi en la consola). En el tipo de recuperación diferida también, sucede lo mismo. Entonces, ¿cuál es la diferencia?
Neha Choudhary

Las colecciones ansiosas se obtienen cuando se carga la entidad propietaria. Las colecciones perezosas se obtienen cuando accede a ellas. Si este no es el comportamiento que vio, probablemente haya algo mal en su entorno (por ejemplo, ejecutar versiones antiguas de una clase)
Bozho

1
@Bozho Solo especificó la carga diferida de colecciones. ¿Se puede cargar un campo de cadena simple de forma diferida?
vikiiii

No.
Debe

@Bozho, oye, ¿puedes responder esto? Entonces, si está configurado en fetchtype = LAZYel predeterminado, incluso si intenta obtener la colección con el getter hibernete arroja un error diciéndome que no puede evaluar
Все Едно

16

Puedo considerar el rendimiento y la utilización de la memoria. Una gran diferencia es que la estrategia de recuperación EAGER permite utilizar objetos de datos recuperados sin sesión. ¿Por qué?
Todos los datos se obtienen cuando los datos marcados ansiosos en el objeto cuando se conecta la sesión. Sin embargo, en el caso de una estrategia de carga diferida, el objeto marcado de carga diferida no recupera datos si la sesión se desconecta (después de la session.close()declaración). Todo lo que puede hacer el proxy de hibernación. La estrategia entusiasta permite que los datos sigan disponibles después de cerrar la sesión.


11

Según mi conocimiento, ambos tipos de búsqueda dependen de sus requisitos.

FetchType.LAZY está bajo demanda (es decir, cuando requerimos los datos).

FetchType.EAGER es inmediato (es decir, antes de que llegue nuestro requisito, estamos recuperando el registro innecesariamente)


11

De forma predeterminada, para todos los objetos de colección y mapa, la regla de obtención es FetchType.LAZYy para otras instancias sigue la FetchType.EAGERpolítica.
En resumen, @OneToManyy las @ManyToManyrelaciones no obtienen los objetos relacionados (colección y mapa) implícitamente, pero la operación de recuperación se conecta en cascada a través del campo en @OneToOney @ManyToOneunos.

(cortesía: - objectdbcom)


9

Ambos FetchType.LAZYy FetchType.EAGERse utilizan para definir el plan de recuperación predeterminado .

Desafortunadamente, solo puede anular el plan de recuperación predeterminado para la recuperación PERFECTA. La obtención de EAGER es menos flexible y puede generar muchos problemas de rendimiento .

Mi consejo es contener el impulso de hacer que sus asociaciones sean EAGER porque buscar es una responsabilidad de tiempo de consulta. Por lo tanto, todas sus consultas deben usar la directiva fetch para recuperar solo lo necesario para el caso de negocios actual.


2
"La obtención de EAGER es menos flexible y puede generar muchos problemas de rendimiento". ... Una declaración más verdadera es "Usar o no usar la recuperación EAGER puede conducir a problemas de rendimiento". En ese caso particular, cuando un campo vagamente inicializado es costoso de acceder Y se usa con poca frecuencia, la recuperación perezosa beneficiará el rendimiento. Pero, en el caso de que una variable se use con frecuencia, la inicialización diferida en realidad puede degradar el rendimiento al requerir más viajes a la base de datos que la inicialización ansiosa. Sugeriría aplicar FetchType correctamente, no dogmáticamente.
scottb

¿Estás promocionando tu libro aquí! Pero sí, creo que depende del caso de uso y del tamaño del objeto al que se hace referencia en la relación de cardinalidad.
John Doe

6

Del Javadoc :

La estrategia EAGER es un requisito en el tiempo de ejecución del proveedor de persistencia de que los datos se deben obtener con entusiasmo. La estrategia LAZY es una pista para el tiempo de ejecución del proveedor de persistencia de que los datos deben recuperarse perezosamente cuando se accede por primera vez.

Por ejemplo, ansioso es más proactivo que perezoso. Lazy solo ocurre en el primer uso (si el proveedor toma la indirecta), mientras que con cosas ansiosas (puede) pretrabarse.


1
¿Qué quieres decir con "primer uso"?
Leon

@leon: Digamos que tiene una entidad con un campo ansioso y un campo perezoso. Cuando obtenga la entidad, el campo ansioso se habrá cargado desde la base de datos en el momento en que reciba la referencia de la entidad, pero el campo perezoso puede no haber sido. Se recuperaría solo cuando intentara acceder al campo a través de su descriptor de acceso.
TJ Crowder

@TJ Crowder, ¿cuál es el valor predeterminado cuando no se define fetchtype?
Mahmoud Saleh

@MahmoudSaleh: No tengo idea. Probablemente varía según algo. No he usado JPA en un proyecto real, así que no me he metido en las entrañas.
TJ Crowder

2
@MahmoudS: Tipos de fetch predeterminados: OneToMany: LAZY, ManyToOne: EAGER, ManyToMany: LAZY, OneToOne: EAGER, Columnas: EAGER
Markus Pscheidt

5

El Lazytipo de recuperación se selecciona de forma predeterminada por Hibernate a menos que marque explícitamente el Eagertipo de recuperación. Para ser más exacto y conciso, la diferencia se puede establecer de la siguiente manera.

FetchType.LAZY = Esto no carga las relaciones a menos que lo invoque a través del método getter.

FetchType.EAGER = Esto carga todas las relaciones.

Pros y contras de estos dos tipos de búsqueda.

Lazy initialization mejora el rendimiento al evitar cálculos innecesarios y reduce los requisitos de memoria.

Eager initialization requiere más consumo de memoria y la velocidad de procesamiento es lenta.

Dicho esto, depende de la situación, cualquiera de estas inicializaciones puede ser utilizada.


1
Es importante tener en cuenta la afirmación de que "no carga las relaciones a menos que lo invoque a través del método getter", y también una decisión de diseño bastante retrasada en mi opinión ... Acabo de encontrar un caso en el que supuse que lo recuperaría al acceder y no lo hizo, porque no llamé explícitamente una función getter para ello. Por cierto, ¿qué constituye una función "getter"? ¿Aplazará JPA la carga de la propiedad hasta que getMemberse llame a una función llamada que coincida exactamente con el patrón de nombre del miembro?
ToVine

3

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;
        }

        }

Sujeto.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();
        }
    }

    }

Verifique el método retrieve () de Main.java. Cuando obtengamos Asunto, su lista de colección Libros , anotada con @OneToMany, se cargará perezosamente. Pero, por otro lado, la asociación de libros relacionada con el tema de la colección , anotada con @ManyToOne, se carga de manera importante (por [default][1]para @ManyToOne, fetchType=EAGER). Podemos cambiar el comportamiento colocando fetchType.EAGER en @OneToManySubject.java o fetchType.LAZY @ManyToOneen Books.java.


1

public enum FetchType extiende java.lang.Enum Define estrategias para obtener datos de la base de datos. La estrategia EAGER es un requisito en el tiempo de ejecución del proveedor de persistencia de que los datos se deben obtener con entusiasmo. La estrategia LAZY es una pista para el tiempo de ejecución del proveedor de persistencia de que los datos deben recuperarse perezosamente cuando se accede por primera vez. La implementación está autorizada a buscar datos para los cuales se ha especificado la sugerencia de estrategia LAZY. Ejemplo: @Basic (fetch = LAZY) protegido String getName () {return name; }

Fuente


1

Quiero agregar esta nota a lo que "Kyung Hwan Min" dijo anteriormente.

Supongamos que está utilizando Spring Rest con este simple arquitecto:

Controlador <-> Servicio <-> Repositorio

Y desea devolver algunos datos al front-end, si está utilizando FetchType.LAZY, obtendrá una excepción después de devolver los datos al método del controlador ya que la sesión se cierra en el Servicio, por lo JSON Mapper Objectque no puede obtener los datos.

Hay tres opciones comunes para resolver este problema, depende del diseño, el rendimiento y el desarrollador:

  1. La más fácil de usar es FetchType.EAGER, para que la sesión siga viva en el método del controlador.
  2. Soluciones antipatrones , para hacer que la sesión viva hasta que finalice la ejecución, es un gran problema de rendimiento en el sistema.
  3. La mejor práctica es usar el FetchType.LAZYmétodo convertidor para transferir datos desde Entityotro objeto de datos DTOy enviarlo al controlador, por lo que no hay excepción si la sesión se cierra.


0

@ drop-shadow si está utilizando Hibernate, puede llamar Hibernate.initialize()cuando invoque el getStudents()método:

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

0

Perezoso: recupera las entidades secundarias de forma perezosa, es decir, al momento de recuperar la entidad principal, solo obtiene el proxy (creado por cglib o cualquier otra utilidad) de las entidades secundarias y cuando accede a cualquier propiedad de la entidad secundaria, en realidad es recuperado por hibernación.

EAGER: recupera las entidades secundarias junto con la principal.

Para una mejor comprensión, vaya a la documentación de Jboss o puede usar hibernate.show_sql=truesu aplicación y verificar las consultas emitidas por la hibernación.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.