CodeGym /Courses /Module 5. Spring /Lazy and Eager data fetching (Lazy and Eager Fetching)

Lazy and Eager data fetching (Lazy and Eager Fetching)

Module 5. Spring
Level 5 , Lesson 9
Available

Working with objects in JPA lets us forget the "dirty" work of SQL queries and work with familiar Java objects. But when you fetch related data an important question shows up: when should you load it? JPA gives you two approaches for that: Lazy (lazy) and Eager (eager) fetching.

How does this work in practice?

Imagine an order service where each order has a list of products:

  • Eager loading (Eager) — when you request an order, JPA will automatically load all related products. Convenient, but if you have a thousand orders and you only need their IDs you'll end up transferring a ton of unnecessary data.
  • Lazy loading (Lazy) — JPA will load only the main order info. Related products are loaded only when you explicitly access them. That's efficient, but you need to be careful about when the data is loaded.

Choosing between Lazy and Eager is always a trade-off between convenience and performance. Often the right choice depends on the exact use case.

Fetch type configurations

JPA offers two ways to fetch related data, and you pick one using the fetch attribute.

The fetch attribute in JPA


@ManyToOne(fetch = FetchType.LAZY)
private User user;
  • FetchType.LAZY — lazy loading: data is pulled only when it's actually needed.
  • FetchType.EAGER — eager loading: all related data is loaded immediately.

Let's look at a real online store example:


@Entity
public class Order {

    @Id
    @GeneratedValue
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    private Customer customer;

    @OneToMany(mappedBy = "order", fetch = FetchType.EAGER)
    private List<Product> products;

    // getters and setters
}

What's happening here:

  • Customer info (Customer) is loaded lazily. Makes sense: you don't always need customer data when you're working with an order.
  • Products (Product) are loaded immediately — an order without its product list doesn't make sense!

Differences between Lazy and Eager

Lazy Loading Eager Loading
When it fetches data When they're accessed Right away when the parent object is loaded
Performance Saves resources if related data isn't used Can lead to excessive queries
Implementation complexity You can hit errors like LazyInitializationException Works "out of the box"
Usage Recommended for most operations Use only if you're sure the data is always needed

Practice: Lazy fetching in action

Entity example: Customer (Customer) and Orders (Order)

Let's slightly complicate our database app. We already have Customer and Order entities, and we'll implement a one-to-many relationship between a customer and their orders.

Entity Customer


@Entity
public class Customer {

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

    private String name;

    @OneToMany(mappedBy = "customer", fetch = FetchType.LAZY)
    private List<Order> orders = new ArrayList<>();

    // getters and setters
}

Entity Order


@Entity
public class Order {

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

    private String description;

    @ManyToOne(fetch = FetchType.LAZY)
    private Customer customer;

    // getters and setters
}

We see both sides are set to Lazy. Now let's try to fetch data from the database.

Repositories


@Repository
public interface CustomerRepository extends JpaRepository<Customer, Long> { }

@Repository
public interface OrderRepository extends JpaRepository<Order, Long> { }

Service class


@Service
public class CustomerService {

    @Autowired
    private CustomerRepository customerRepository;

    public void printCustomerOrders(Long customerId) {
        Customer customer = customerRepository.findById(customerId).orElseThrow();

        System.out.println("Customer: " + customer.getName());
        System.out.println("Orders: " + customer.getOrders());
    }
}

The LazyInitializationException

Try calling printCustomerOrders(), and... BAM! An error!

LazyInitializationException tells us that the Hibernate context has already closed the connection to the database, and we can't load lazy data. Why? We're trying to access customer.getOrders() outside of a transaction.

How to avoid this error?

  1. Use Eager loading. Just change fetch to FetchType.EAGER. But that's not always justified.
  2. Manual initialization. Use Hibernate.initialize() to load lazy data.
    
    @Transactional
    public void printCustomerOrders(Long customerId) {
        Customer customer = customerRepository.findById(customerId).orElseThrow();
        Hibernate.initialize(customer.getOrders());
        System.out.println("Customer: " + customer.getName());
        System.out.println("Orders: " + customer.getOrders());
    }
    

  3. Or (my favorite): JOIN FETCH. Use a query with JOIN FETCH to preload the data.
    
    @Query("SELECT c FROM Customer c JOIN FETCH c.orders WHERE c.id = :id")
    Customer findCustomerWithOrders(@Param("id") Long id);
    

    And in the method just:

    
    public void printCustomerOrders(Long customerId) {
        Customer customer = customerRepository.findCustomerWithOrders(customerId);
        System.out.println("Customer: " + customer.getName());
        System.out.println("Orders: " + customer.getOrders());
    }
    

Why this matters

When you're designing complex apps, the fetch type can seriously affect performance. For example, if a system has thousands of entities, eager loading can drown your database in tons of unnecessary queries. On the other hand, a poorly set up lazy strategy can cause a bunch of runtime surprises.


Common mistakes and how to avoid them

  • Abusing Eager loading: this is usually a beginner mistake. They think "the more data I load, the better." But if the data isn't used, it's wasted resources.
  • Missing a transaction with Lazy loading: make sure lazy data is always loaded within a transaction.
  • Request overload with Lazy loading: if lazy data is loaded inside a loop, you can end up with many separate SQL queries. Use JOIN FETCH to combine them into a single query.

Practical use

In interviews they'll definitely ask about the difference between Lazy and Eager. Knowing how to avoid lazy-related mistakes and when to use eager loading (in a good way) is a great chance to impress.

In real projects where you deal with lots of related data, proper fetch configuration is critical for performance. For example, social networks where users are connected to tons of stuff — friends, posts, comments — require smart choices about fetching.


So let's move on and don't be lazy, so we don't get lazy exceptions!

Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION