Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Lazy and eager loading

When you load a post, Hibernate has to decide whether to load its author and comments right away. Eager loading brings them in the same query or immediately after. Lazy loading postpones it: related data is fetched only when the code touches it. The choice is made with the fetch parameter of a relationship annotation.

The blog model from now on

From this lesson on, the examples use the full model: Author, Post, Comment and Tag. The relationships of Post:

// src/main/java/com/example/blog/Post.java
private String title;
 
private int views;
 
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "author_id")
private Author author;
 
@OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Comment> comments = new ArrayList<>();
 
@ManyToMany
@JoinTable(name = "post_tag",
        joinColumns = @JoinColumn(name = "post_id"),
        inverseJoinColumns = @JoinColumn(name = "tag_id"))
private Set<Tag> tags = new HashSet<>();

Comment also refers to its post with @ManyToOne(fetch = FetchType.LAZY). Test data is created by a helper class:

// src/main/java/com/example/blog/Seed.java
package com.example.blog;
 
import jakarta.persistence.EntityManagerFactory;
 
public class Seed {
    public static void run(EntityManagerFactory emf) {
        emf.runInTransaction(em -> {
            Author anna = new Author("Anna");
            Author oleg = new Author("Oleg");
            Author maria = new Author("Maria");
            em.persist(anna);
            em.persist(oleg);
            em.persist(maria);
 
            Tag java = new Tag("java");
            Tag hibernate = new Tag("hibernate");
            em.persist(java);
            em.persist(hibernate);
 
            Post p1 = new Post("Hello, Hibernate", anna);
            p1.setViews(120);
            p1.getTags().add(java);
            p1.getTags().add(hibernate);
            p1.addComment(new Comment("Great post!"));
            p1.addComment(new Comment("Thanks, Anna"));
 
            Post p2 = new Post("Records in Java 25", oleg);
            p2.setViews(80);
            p2.getTags().add(java);
            p2.addComment(new Comment("Very clear"));
 
            Post p3 = new Post("Lazy loading", maria);
            p3.setViews(45);
            p3.getTags().add(hibernate);
 
            em.persist(p1);
            em.persist(p2);
            em.persist(p3);
        });
    }
}

Watching the queries

// src/main/java/com/example/blog/Main.java
Seed.run(emf);
emf.runInTransaction(em -> {
    Post post = em.find(Post.class, 1L);
    IO.println("Title: " + post.getTitle());
    IO.println("Author class: " + post.getAuthor().getClass().getSimpleName());
    IO.println("Author: " + post.getAuthor().getName());
    IO.println("Comments: " + post.getComments().size());
});
Hibernate: select p1_0.id,p1_0.author_id,p1_0.title,p1_0.views from Post p1_0 where p1_0.id=?
Title: Hello, Hibernate
Author class: Author$HibernateProxy
Hibernate: select a1_0.id,a1_0.name from Author a1_0 where a1_0.id=?
Author: Anna
Hibernate: select c1_0.post_id,c1_0.id,c1_0.text from Comment c1_0 where c1_0.post_id=?
Comments: 2

find loaded only the post. Instead of the author, Hibernate put a proxy into the field — a stand-in subclass that knows only the id. The real query ran at the first getName(). The comments were loaded at the first size().

With @ManyToOne without fetch = FetchType.LAZY the author arrives at once, through a join:

Hibernate: select p1_0.id,a1_0.id,a1_0.name,p1_0.title,p1_0.views from Post p1_0 left join Author a1_0 on a1_0.id=p1_0.author_id where p1_0.id=?

Defaults

RelationshipDefault
@ManyToOne, @OneToOneEAGER
@OneToMany, @ManyToManyLAZY

The usual advice is to make every relationship lazy — that is, to add fetch = FetchType.LAZY to @ManyToOne and @OneToOne — and to load related data explicitly where it is needed. How to do that is the topic of the next lesson.

Common mistake

A lazy relationship can be loaded only while the EntityManager is open. Outside the transaction you get an error:

// src/main/java/com/example/blog/Main.java
Post post = emf.callInTransaction(em -> em.find(Post.class, 1L));
IO.println("Author loaded: " + Hibernate.isInitialized(post.getAuthor()));
IO.println("Author: " + post.getAuthor().getName());
Author loaded: false
Exception in thread "main" org.hibernate.LazyInitializationException: Could not initialize proxy [com.example.blog.Author#1] - no session

Hibernate.isInitialized checks whether the data has been loaded. Load everything you will need inside the transaction — with join fetch or an entity graph from the next lesson.

Thanks for reading!

Donate to the site — we work hard for you.