Spring Boot: From Basics to Production · Persistence with Spring Data JPA
Relationships and the N+1 Problem
Map associations sensibly and fetch them efficiently.
Map associations with @ManyToOne (make it LAZY — the default is EAGER), @OneToMany(mappedBy=...) and avoid @ManyToMany for anything with extra data (use an explicit join entity). Lazy associations accessed in loops cause N+1 queries. Fix with join fetch queries, @EntityGraph, or batch fetching (hibernate.default_batch_fetch_size). Disable open-in-view so lazy loading can't silently happen in controllers.
@Entity
public class Order {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
private Customer customer;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
private List<OrderLine> lines = new ArrayList<>();
}
public interface OrderRepository extends JpaRepository<Order, Long> {
@EntityGraph(attributePaths = {"customer", "lines"})
List<Order> findByStatus(OrderStatus status);
@Query("select distinct o from Order o join fetch o.lines where o.id = :id")
Optional<Order> findWithLines(long id);
}
spring:
jpa:
open-in-view: false
properties:
hibernate.default_batch_fetch_size: 50
Common pitfalls
- EAGER @ManyToOne defaults loading huge graphs.
- Fetch-joining multiple collections (cartesian products, MultipleBagFetchException).
- open-in-view hiding N+1 queries until production load.
Key points
- LAZY by default; fetch explicitly
- Entity graphs / join fetch / batch fetching
- Disable open-in-view
Further reading: Spring Data JPA: Entity graphs
Practice questions
What is open-in-view and why disable it?
Answer
It keeps the Hibernate session open during view rendering so lazy loading works in controllers, but it hides N+1 queries and holds connections longer. Disabling it forces explicit fetching in services.
Why is fetch-joining two collections in one query problematic?
Answer
It produces a cartesian product of rows (and Hibernate may throw MultipleBagFetchException for lists); fetch one collection and batch-load the other.
Open in the app to mark lessons complete, take notes and track your progress.