616
votes

I am a newbie to Java Persistence API and Hibernate.

What is the difference between FetchType.LAZY and FetchType.EAGER in Java Persistence API?

16
EAGER loading of collections means that they are fetched fully at the time their parent is fetched. While EAGER loading, then all my child is fetched. The child is fetched in the PersistentSet and PersistentList (or PersistentBag), inside the Persistent Bag , it displayed as an Array List. Is it correct??..geetha

16 Answers

1185
votes

Sometimes you have two entities and there's a relationship between them. For example, you might have an entity called University and another entity called Student and a University might have many Students:

The University entity might have some basic properties such as id, name, address, etc. as well as a collection property called students that returns the list of students for a given university:

A university has many students

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

   // setters and getters
}

Now when you load a University from the database, JPA loads its id, name, and address fields for you. But you have two options for how students should be loaded:

  1. To load it together with the rest of the fields (i.e. eagerly), or
  2. To load it on-demand (i.e. lazily) when you call the university's getStudents() method.

When a university has many students it is not efficient to load all of its students together with it, especially when they are not needed and in suchlike cases you can declare that you want students to be loaded when they are actually needed. This is called lazy loading.

Here's an example, where students is explicitly marked to be loaded eagerly:

@Entity
public class University {

    @Id
    private String id;

    private String name;

    private String address;

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

    // etc.    
}

And here's an example where students is explicitly marked to be loaded lazily:

@Entity
public class University {

    @Id
    private String id;

    private String name;

    private String address;

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

    // etc.
}
312
votes

Basically,

LAZY = fetch when needed
EAGER = fetch immediately
71
votes

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)

19
votes

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.

11
votes

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)

11
votes

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.

(courtesy :- objectdbcom)

11
votes

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.

8
votes

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.

7
votes

The main difference between the two types of fetching is a moment when data gets loaded into a memory.
I have attached 2 photos to help you understand this.

Eager fetch
 Eager fetch

Lazy fetch Lazy fetch

6
votes

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.

4
votes

I want to add this note to what said above.

Suppose you are using Spring (MVC and Data) 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:

  1. The easiest one is to use FetchType.EAGER, So that the session will still alive at the controller method, but this method will impact the performance.
  2. Anti-patterns solutions, to make the session live until the execution ends, it is make a huge performance issue in the system.
  3. The best practice is to use 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.
3
votes

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.

1
votes

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

Source

1
votes

JOIN is the big deal

Take it in easy way :

assume we have a class called User and another class called an Address and suppose each user have one or more addresses that mean relationship (one-to-many) here if you execute :

FetchType.LAZY execute sql command like without join :

SELECT * FROM users 

FetchType.EAGER execute sql command like within join :

SELECT * FROM users u join address a on a.user_id = u.user_id

Note : the above queries just for clarify image for you but Hibernate framework in realty execute similar queries of above quires .

Which Fetch Types is Better?

  • Since Eager fetching loads ALL relationships automatically, It's a big performance hog
  • Lazy fetching doesn't load any relationships unless told to, which leads to better performance
  • Eager fetching makes for easier programming, since less code is required
  • Lazy loading could lead to bugs (exceptions) If the entire system Isn't properly tested
  • All things considered, you should still prefer Lazy loading over Eager, as it's more performant

If you are using Spring Boot Framework so going to application.properties file and add the below command to know what exactly going on .

logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
0
votes

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

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.