I am a newbie to Java Persistence API and Hibernate.
What is the difference between FetchType.LAZY
and FetchType.EAGER
in Java Persistence API?
This question is related to
java
hibernate
jpa
orm
java-persistence-api
Both FetchType.LAZY
and FetchType.EAGER
are used to define the default fetch plan.
Unfortunately, you can only override the default fetch plan for LAZY fetching. EAGER fetching is less flexible and can lead to many performance issues.
My advice is to restrain the urge of making your associations EAGER because fetching is a query-time responsibility. So all your queries should use the fetch directive to only retrieve what's necessary for the current business case.
@drop-shadow if you're using Hibernate, you can call Hibernate.initialize()
when you invoke the getStudents()
method:
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;
}
//...
}
public enum FetchType extends java.lang.Enum Defines strategies for fetching data from the database. The EAGER strategy is a requirement on the persistence provider runtime that data must be eagerly fetched. The LAZY strategy is a hint to the persistence provider runtime that data should be fetched lazily when it is first accessed. The implementation is permitted to eagerly fetch data for which the LAZY strategy hint has been specified. Example: @Basic(fetch=LAZY) protected String getName() { return name; }
From the Javadoc:
The EAGER strategy is a requirement on the persistence provider runtime that data must be eagerly fetched. The LAZY strategy is a hint to the persistence provider runtime that data should be fetched lazily when it is first accessed.
E.g., eager is more proactive than lazy. Lazy only happens on first use (if the provider takes the hint), whereas with eager things (may) get pre-fetched.
The Lazy
Fetch type is by default selected by Hibernate unless you explicitly mark Eager
Fetch type. To be more accurate and concise, difference can be stated as below.
FetchType.LAZY
= This does not load the relationships unless you invoke it via the getter method.
FetchType.EAGER
= This loads all the relationships.
Pros and Cons of these two fetch types.
Lazy initialization
improves performance by avoiding unnecessary computation and reduce memory requirements.
Eager initialization
takes more memory consumption and processing speed is slow.
Having said that, depends on the situation either one of these initialization can be used.
LAZY: It fetches the child entities lazily i.e at the time of fetching parent entity it just fetches proxy(created by cglib or any other utility) of the child entities and when you access any property of child entity then it is actually fetched by hibernate.
EAGER: it fetches the child entities along with parent.
For better understanding go to Jboss documentation or you can use hibernate.show_sql=true
for your app and check the queries issued by the hibernate.
As per my knowledge both type of fetch depends your requirement.
FetchType.LAZY
is on demand (i.e. when we required the data).
FetchType.EAGER
is immediate (i.e. before our requirement comes we are unnecessarily fetching the record)
EAGER
loading of collections means that they are fetched fully at the time their parent is fetched. So if you have Course
and it has List<Student>
, all the students are fetched from the database at the time the Course
is fetched.
LAZY
on the other hand means that the contents of the List
are fetched only when you try to access them. For example, by calling course.getStudents().iterator()
. Calling any access method on the List
will initiate a call to the database to retrieve the elements. This is implemented by creating a Proxy around the List
(or Set
). So for your lazy collections, the concrete types are not ArrayList
and HashSet
, but PersistentSet
and PersistentList
(or PersistentBag
)
I may consider performance and memory utilization. One big difference is that EAGER fetch strategy allows to use fetched data object without session. Why?
All data is fetched when eager marked data in the object when session is connected. However, in case of lazy loading strategy, lazy loading marked object does not retrieve data if session is disconnected (after session.close()
statement). All that can be made by hibernate proxy. Eager strategy lets data to be still available after closing session.
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();
}
}
}
Check the retrieve() method of Main.java. When we get Subject, then its collection listBooks, annotated with @OneToMany
, will be loaded lazily. But, on the other hand, Books related association of collection subject, annotated with @ManyToOne
, loads eargerly (by [default][1]
for @ManyToOne
, fetchType=EAGER
). We can change the behaviour by placing fetchType.EAGER on @OneToMany
Subject.java or fetchType.LAZY on @ManyToOne
in Books.java.
Basically,
LAZY = fetch when needed
EAGER = fetch immediately
I want to add this note to what said above.
Suppose you are using Spring Rest with this simple architect:
Controller <-> Service <-> Repository
And you want to return some data to the front-end, if you are using FetchType.LAZY
, you will get an exception after you return data to the controller method since the session is closed in the Service so the JSON Mapper Object
can't get the data.
There is three common options to solve this problem, depends on the design, performance and the developer:
FetchType.EAGER
, So that the session will still alive at the controller method, but this method will impact the performance.FetchType.LAZY
with mapper like MapStruct
to transfer data from Entity
to another data object DTO
and then send it back to the controller, so there is no exception if the session closed.By default, for all collection and map objects the fetching rule is FetchType.LAZY
and for other instances it follows the FetchType.EAGER
policy.
In brief, @OneToMany
and @ManyToMany
relations does not fetch the related objects (collection and map) implicictly but the retrieval operation is cascaded through the field in @OneToOne
and @ManyToOne
ones.
Source: Stackoverflow.com