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.