ChatGPT解决这个技术问题 Extra ChatGPT

Difference between FetchType LAZY and EAGER in Java Persistence API?

I am a newbie to Java Persistence API and Hibernate.

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

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??..

B
Behrang

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:

https://i.stack.imgur.com/N1PL3.png

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:

To load it together with the rest of the fields (i.e. eagerly), or 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.
}

@BehrangSaeedzadeh can you list some practical differences, or the advantages and disadvantages of each type of loading (other than the efficiency you mentioned). Why would one want to use eager loading?
@ADTC In order for lazy loading to work, the JDBC session must still be open when the target entities want to be loaded into the memory by invoking the getter method (e.g. getStudents()), but sometimes this is not possible, because by the time this method is called, the session is already closed and the entity detached. Similarly, sometimes we have a client/server architecture (e.g. Swing client/JEE server) and the entities/DTOs are transferred over the wire to the client and again most often in these scenarios lazy loading won't work due to the way the entities are serialized over the wire.
I'd like to add some more info to this answer from my book - To save memory, Lazy loading is generally used for one to many and many to many relationships. For one to one, generally Eager is used.
In lazy loading, when I call the getStudents() method for the first time, are the results cached? so that I could access those results faster next time?
@JavaTechnical depends if you enable the second level cache (enabled by default)
u
unbeli

Basically,

LAZY = fetch when needed
EAGER = fetch immediately

B
Bozho

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 used that concept in fetching the details of a child entity, but I can not see any difference between them. When I specify Eager fetch, It fetches everything and When I debug it, I see "Bean deferred" at the child entity. When I say course.getStudents(), it fires an SQL query( saw that on console). In Lazy fetch type also, same thing happens. So, What's is the difference??
eager collections are fetched when the owning entity is loaded. Lazy collections are fetched when you access them. If this is not the behaviour your saw, there was probably something wrong with your environment (e.g. running old versions of a class)
@Bozho You specified lazy loading of collections only . Can a simple string field be lazy loaded?
No. You need to use a query or a different mapped entity to get a subset of the columns
@Bozho, hey can you please answer this then if it's set on fetchtype = LAZY the default one even if try to get the collection with the getter hibernete throws an error telling me it can't evaluate
r
rtruszk

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.


Yes and it crashed my app until I switched to eager. I suppose session management is an art and science itself but JawsDB gives 10 connections on free tier and not much more on paid tiers.
A
Ali Yeganeh

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.

https://i.stack.imgur.com/ws0yq.jpg

https://i.stack.imgur.com/e0EFV.jpg


r
rtruszk

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)


J
JDGuide

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)


V
Vlad Mihalcea

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.


"EAGER fetching is less flexible and can lead to many performance issues." ... A truer statement is "Using or not using EAGER fetching can lead to performance problems". In that particular case when a lazily initialized field is expensive to access AND infrequently used, lazy fetching will benefit performance. But, in the case when a variable is frequently used, lazy initialization can actually degrade performance by requiring more trips to the database than eager initialization. I would suggest applying FetchType correctly, not dogmatically.
Are you promoting your book here!!. But yes I feel it depends on the use case, and the object size being referred to in the cardinality relationship.
D
Du-Lacoste

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.


The statement that it "does not load the relationships unless you invoke it via the getter method" is important to note, and also a pretty retarded design decision in my opininion… I just encountered a case where I assumed it would fetch it upon access and it didn't, because I didn't excplicitly call a getter function for it. By the way, what constitutes a "getter" function? Will JPA defer loading the property until a function called getMember is called that exactly matches the member's name pattern?
E
Ebraheem Alrabeea

I want to add this note to what was 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 a LazyInitializationException 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 are two common options to solve this problem, depending on the design, performance, and the developer:

The easiest one is to use FetchType.EAGER or any other Anti-patterns solutions, So that the session will still be alive at the controller method, but these methods will impact the performance. The best practice is to use FetchType.LAZY with a 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.

There is a simple example:

@RestController
@RequestMapping("/api")
public class UserResource {

    @GetMapping("/users")
    public Page<UserDTO> getAllUsers(Pageable pageable) {
        return userService.getAllUsers(pageable);
    }
}

@Service
@Transactional
public class UserService {

    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Transactional(readOnly = true)
    public Page<UserDTO> getAllUsers(Pageable pageable) {
        return userRepository.findAll(pageable).map(UserDTO::new);
    }
}

@Repository
public interface UserRepository extends JpaRepository<User, String> {

    Page<User> findAll(Pageable pageable);
}

public class UserDTO {

    private Long id;

    private String firstName;

    private String lastName;

    private String email;
    
    private Set<String> addresses;

    public UserDTO() {
        // Empty constructor needed for Jackson.
    }

    public UserDTO(User user) {
        this.id = user.getId();
        this.firstName = user.getFirstName();
        this.lastName = user.getLastName();
        this.email = user.getEmail();
        this.addresses = user.getAddresses().stream()
            .map(Address::getAddress)
            .collect(Collectors.toSet());
    }

    // Getters, setters, equals, and hashCode
}

@Entity
@Table(name = "user")
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column
    private String firstName;

    @Column
    private String lastName;

    @Column(unique = true)
    private String email;
    
    @OneToMany(mappedBy = "address", fetch = FetchType.LAZY)
    private Set<Address> addresses = new HashSet<>();

    // Getters, setters, equals, and hashCode
}

@Entity
@Table(name = "address")
public class Address implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column
    private String address;

    @ManyToOne
    @JsonIgnoreProperties(value = "addresses", allowSetters = true)
    private User user;

    // Getters, setters, equals, and hashCode
}

Could you pls give an example on how to implement the 3rd solution, e.g. in @Behrang's answer?
@Naik, I have updated the answer and added an example.
T
T.J. Crowder

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.


what do you mean by "first use"?
@leon: Say you have an entity with an eager field and a lazy field. When you get the entity, the eager field will have been loaded from the DB by the time you receive the entity reference, but the lazy field may not have been. It would be fetched only when you tried to access the field via its accessor.
@T.J. Crowder, what's the default when no fetchtype defined ?
@MahmoudSaleh: I have no idea. It probably varies based on something. I haven't used JPA in a real project so I haven't gotten into the guts of it.
@MahmoudS: Default fetchtypes: OneToMany: LAZY, ManyToOne: EAGER, ManyToMany: LAZY, OneToOne: EAGER, Columns : EAGER
K
Khalil M

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.


A
Abd Abughazaleh

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

d
debugmode

The best way to understand the difference between these is if you understand lazy-okay. FetchType.LAZY tells hibernate to only fetch related entities from the database when you use the relationship.

P.S: In many projects I have worked on, I have seen that many software developers do not pay attention to them, and there are even those who call themselves senior.If the project you're working on isn't data-exchanging on large volumes of data, it's okay to have EAGER here. However, considering the problems where n+1 problems may occur, you need to pay attention to these after you know the fetch types of the relations by default.

Here you can see the default values: Default fetch type for one-to-one, many-to-one and one-to-many in Hibernate

Also, it doesn't end there, even after understanding fetching types. To understand when to use LAZY and when to use EAGER, you also need to understand the concepts of unidirectional and bidirectional. Also, the spring boot repository has some methods that allow it to read data for you lazy or eager.For example, the getOne() or getById() methods allow you to lazy fetch data from entities.In short, what you use and when depends on what the other party wants you to do.


A
Anas Lachheb

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


r
rtruszk

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

m
marc_s

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.