¿Diferencia entre fetchtype perezoso y ansioso en la API de persistencia de Java?
-
24-10-2019 - |
Pregunta
Soy un novato en Java Persistence API y Hibernate.
Cuál es la diferencia entre FetchType.LAZY
y FetchType.EAGER
¿En Java Persistence API?
Solución
A veces tienes dos entidades y hay una relación entre ellas. Por ejemplo, es posible que se le llame una entidad llamada University
y otra entidad llamada Student
Y una universidad podría tener muchos estudiantes:
La entidad universitaria puede tener algunas propiedades básicas como ID, nombre, dirección, etc., así como una propiedad de recolección llamada estudiantes que devuelve la lista de estudiantes para 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 su ID, nombre y campos de dirección para usted. Pero tiene dos opciones sobre cómo se deben cargar los estudiantes:
- Cargarlo junto con el resto de los campos (es decir, ansiosamente), o
- Para cargarlo a pedido (es decir, perezosamente) cuando llamas a la universidad
getStudents()
método.
Cuando una universidad tiene muchos estudiantes, no es eficiente cargar a todos sus estudiantes junto con ella, especialmente cuando no son necesarios y, en casos, puede declarar que desea que los estudiantes se carguen cuando realmente sean necesarios. Esto se llama carga perezosa.
Aquí hay un ejemplo, donde students
está explícitamente marcado para ser cargado 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á explícitamente marcado para cargar perezosamente:
@Entity
public class University {
@Id
private String id;
private String name;
private String address;
@OneToMany(fetch = FetchType.LAZY)
private List<Student> students;
// etc.
}
Otros consejos
Básicamente,
LAZY = fetch when needed
EAGER = fetch immediately
EAGER
La carga de colecciones significa que se obtienen completamente en el momento en que sus padres son recortados. Entonces si tienes Course
y tiene List<Student>
, todos los estudiantes son recogidos de la base de datos en ese momento el Course
es recogido.
LAZY
por otro lado significa que el contenido del List
se obtienen solo cuando intentas acceder a ellos. Por ejemplo, llamando course.getStudents().iterator()
. Llamando 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 en torno al List
(o Set
). Entonces, para sus colecciones perezosas, los tipos de concreto no son ArrayList
y HashSet
, pero PersistentSet
y PersistentList
(o PersistentBag
)
Puedo considerar el rendimiento y la utilización de la memoria. Una gran diferencia es que la estrategia de búsqueda ansiosa permite usar el objeto de datos recuperado sin sesión. ¿Por qué?
Todos los datos se obtienen cuando están ansiosos datos marcados en el objeto cuando la sesión está conectada. Sin embargo, en el caso de la estrategia de carga perezosa, el objeto marcado de carga perezosa no recupera los datos si la sesión está desconectada (después session.close()
declaración). Todo lo que se puede hacer por proxy hibernado. La estrategia ansiosa permite que los datos sigan disponibles después de la sesión de cierre.
Por defecto, para todos los objetos de colección y mapa, la regla de recuperación es FetchType.LAZY
y para otros casos sigue el FetchType.EAGER
política.
En breve, @OneToMany
y @ManyToMany
Las relaciones no obtienen los objetos relacionados (colección y mapa) implícitamente, pero la operación de recuperación se cascan a través del campo en @OneToOne
y @ManyToOne
unos.
Según mi conocimiento, ambos tipo de búsqueda depende de su requisito.
FetchType.LAZY
está a pedido (es decir, cuando requerimos los datos).
FetchType.EAGER
es inmediato (es decir, antes de que llegue nuestro requisito, estamos obteniendo innecesariamente el registro)
Ambas cosas FetchType.LAZY
y FetchType.EAGER
se utilizan para definir el Plan de búsqueda predeterminado.
Desafortunadamente, solo puede anular el plan de recuperación predeterminado para la obtención de fisos. La búsqueda ansiosa es menos flexible y puede conducir a muchos problemas de rendimiento.
Mi consejo es restringir el impulso de hacer que sus asociaciones tengan ansiosos porque buscar es una responsabilidad de tiempo de consulta. Entonces todas sus consultas deben usar el buscar Directiva para recuperar solo lo que es necesario para el caso comercial actual.
Desde el Javadoc:
La estrategia ansiosa es un requisito en el tiempo de ejecución del proveedor de persistencia de que los datos deben ser obtenidos ansiosos. La estrategia perezosa 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 pista), mientras que con las cosas ansiosas (mayo) se vuelve precipitada.
los Lazy
El tipo de búsqueda se selecciona por defecto por Hibernate a menos que marque explícitamente Eager
Tipo de búsqueda. Para ser más precisos y concisos, la diferencia se puede establecer como a continuación.
FetchType.LAZY
= Esto no carga las relaciones a menos que las invoce 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 evitando el cálculo innecesario y reduce los requisitos de memoria.
Eager initialization
Toma más consumo de memoria y velocidad de procesamiento es lenta.
Una vez dicho esto, depende de la situación Se puede usar una de estas inicialización.
Libro.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 Remieve () de Main.java. Cuando tenemos sujeto, entonces su colección libros de lista, anotado con @OneToMany
, se cargará perezosamente. Pero, por otro lado, la Asociación de Colección relacionada con los libros tema, anotado con @ManyToOne
, carga eurgerly (por [default][1]
por @ManyToOne
, fetchType=EAGER
). Podemos cambiar el comportamiento colocando fetchtype.age en @OneToMany
Sujeto.java o fetchtype.lazy on @ManyToOne
en libros. Java.
Public Enum FetchType extiende java.lang.enum Define estrategias para obtener datos de la base de datos. La estrategia ansiosa es un requisito en el tiempo de ejecución del proveedor de persistencia de que los datos deben ser obtenidos ansiosos. La estrategia perezosa 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 puede obtener datos con entusiasmo para los cuales se ha especificado la estrategia perezosa. Ejemplo: @Basic (fetch = Lazy) String protegido getName () {nombre de retorno; }
Quiero agregar esta nota a lo que dijo "Kyung Hwan Min" anteriormente.
Supongamos que está utilizando el reposo de primavera con este arquitecto simple:
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 está cerrada en el servicio para que el servicio JSON Mapper Object
No puedo obtener los datos.
Hay tres opciones comunes para resolver este problema, depende del diseño, el rendimiento y el desarrollador:
- El más fácil es usar
FetchType.EAGER
, De modo que la sesión seguirá viva en el método del controlador. - Anti-paternos Soluciones, para que la sesión en vivo hasta que finalice la ejecución, es un gran problema de rendimiento en el sistema.
- La mejor práctica es usar
FetchType.LAZY
con método convertidor para transferir datos deEntity
a otro objeto de datosDTO
Y envíelo al controlador, por lo que no hay excepción si la sesión se cerró.
@Drop-Shadow Si estás usando Hibernate, puedes llamar Hibernate.initialize()
Cuando invocas 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;
}
//...
}
LAZAY: Retira a las entidades infantiles perezosamente, es decir, en el momento de obtener entidad matriz, simplemente obtiene proxy (creado por cglib o cualquier otra utilidad) de las entidades infantiles y cuando accede a cualquier propiedad de la entidad infantil, en realidad la hibernación lo consigue.
ANESES: RECUBA las entidades infantiles junto con los padres.
Para una mejor comprensión, vaya a la documentación de JBoss o puede usar hibernate.show_sql=true
para su aplicación y consulte las consultas emitidas por el Hibernate.