Soy un novato en Java Persistence API e Hibernate.
¿Cuál es la diferencia entre FetchType.LAZY
y FetchType.EAGER
en Java Persistence API?
Soy un novato en Java Persistence API e Hibernate.
¿Cuál es la diferencia entre FetchType.LAZY
y FetchType.EAGER
en Java Persistence API?
Respuestas:
A veces tienes dos entidades y hay una relación entre ellas. Por ejemplo, puede tener una entidad llamada University
y 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:
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:
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 students
está 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 students
está 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.
}
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.
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?
EAGER
la carga de colecciones significa que se recuperan completamente en el momento en que se recupera su padre. Entonces, si tiene Course
y tiene List<Student>
, todos los estudiantes se obtienen de la base de datos en el momento en que Course
se obtiene.
LAZY
por otro lado, significa que los contenidos de los List
archivos solo se obtienen cuando intenta acceder a ellos. Por ejemplo, llamando course.getStudents().iterator()
. Llamar a cualquier método de acceso en el List
iniciará 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 ArrayList
y HashSet
, sino PersistentSet
y PersistentList
(o PersistentBag
)
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?
fetchtype = LAZY
el predeterminado, incluso si intenta obtener la colección con el getter hibernete arroja un error diciéndome que no puede evaluar
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.
De forma predeterminada, para todos los objetos de colección y mapa, la regla de obtención es FetchType.LAZY
y para otras instancias sigue la FetchType.EAGER
política.
En resumen, @OneToMany
y las @ManyToMany
relaciones 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 @OneToOne
y @ManyToOne
unos.
Ambos FetchType.LAZY
y FetchType.EAGER
se 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.
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.
El Lazy
tipo de recuperación se selecciona de forma predeterminada por Hibernate a menos que marque explícitamente el Eager
tipo 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.
getMember
se llame a una función llamada que coincida exactamente con el patrón de nombre del miembro?
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 @OneToMany
Subject.java o fetchType.LAZY @ManyToOne
en Books.java.
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; }
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 Object
que no puede obtener los datos.
Hay tres opciones comunes para resolver este problema, depende del diseño, el rendimiento y el desarrollador:
FetchType.EAGER
, para que la sesión siga viva en el método del controlador.FetchType.LAZY
método convertidor para transferir datos desde Entity
otro objeto de datos DTO
y enviarlo al controlador, por lo que no hay excepción si la sesión se cierra.@ 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;
}
//...
}
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=true
su aplicación y verificar las consultas emitidas por la hibernación.