Fetch Strategies in JPA

About

  • In JPA, FetchType defines when and how related entities or fields are loaded from the database when an entity is accessed.

  • It determines the loading behavior of associations (@OneToOne, @OneToMany, @ManyToOne, @ManyToMany) and large fields (@Lob).

  • Fetching means retrieving associated data when we load the main entity.

FetchType Types

There are two types:

FetchType
Description

EAGER

Load the related entity immediately when the main entity is loaded.

LAZY

Load the related entity only when explicitly accessed (on demand).

Default Fetch Types (By Relationship Type)

Depending on the kind of relationship, JPA defines a default FetchType:

Relationship Type
Default FetchType

@ManyToOne

EAGER

@OneToOne

EAGER

@OneToMany

LAZY

@ManyToMany

LAZY

@ElementCollection

LAZY

@Lob fields

LAZY (recommended, but implementation-dependent)

Example

Entity Classes

@Entity
public class Employee {
    @Id
    private Long id;
    
    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    private Department department;
}
@Entity
public class Department {
    @Id
    private Long id;
    
    private String name;
}

Fetching Behavior

Case 1: LAZY Fetching

Employee emp = entityManager.find(Employee.class, 1L);
// Only employee data loaded
emp.getDepartment().getName(); 
// NOW a query is fired to fetch Department when we call getDepartment()

Case 2: EAGER Fetching

@ManyToOne(fetch = FetchType.EAGER)
private Department department;
Employee emp = entityManager.find(Employee.class, 1L);
// Employee + Department loaded together automatically in a JOIN query

Problems With EAGER Fetching

  • Performance Hit: Always brings associated data even if we don’t need it.

  • Multiple Joins: In complex entity graphs, results in huge SQL queries with many joins → slows down the application.

  • Memory Usage: Wastes memory by loading unnecessary data.

  • N+1 Query Problem: When not handled properly, EAGER can result in additional hidden queries.

Problems With LAZY Fetching

  • LazyInitializationException:

    • Happens if we try to access a LAZY field outside the transaction.

    • Because the proxy cannot load the real data anymore — the EntityManager is closed.

  • Extra Queries:

    • Each LAZY access can cause an extra database call if we don't fetch smartly.

    • Can cause N+1 query problems if not optimized.

Solutions and Best Practices

Problem
Solution

Avoid unnecessary EAGER loading

Use FetchType.LAZY by default wherever possible.

Need multiple associations loaded together?

Use JOIN FETCH in JPQL/HQL queries.

Avoid LazyInitializationException

Fetch necessary associations within the transaction or use DTO projections.

Complex graphs?

Consider Entity Graphs to control fetch dynamically.

Heavy fields (e.g., BLOB/CLOB)?

Always LAZY fetch them.

Last updated

Was this helpful?