Java.Hibernate.Middle.Give me a full list of decisions of n + 1 problem

1) Join fetches (JOIN FETCH in JPQL/HQL)

  • Load parent and associated entities in one SQL query.
  • Example:
SELECT DISTINCT a FROM Author a JOIN FETCH a.books

Pros: very effective; simple.

Cons: can cause Cartesian product explosions when fetching multiple collections.

2) Entity graphs (EntityGraph API)

  • Declaratively specify what associations to eagerly fetch without hardcoding joins in queries.
  • Example:
EntityGraph<Author> graph = em.createEntityGraph(Author.class);
graph.addAttributeNodes("books");
em.createQuery("FROM Author", Author.class)
  .setHint("javax.persistence.fetchgraph", graph)
  .getResultList();

Pros: flexible; keeps queries clean.

Cons: JPA 2.1+ only.

3) Batch fetching (@BatchSize or hibernate.default_batch_fetch_size)

  • Loads multiple lazy proxies in batches, reducing N queries to N/batchSize queries.
  • Example:
@OneToMany(fetch = FetchType.LAZY)
@BatchSize(size = 10)
private List<Book> books;

Pros: keeps lazy loading; effective for many-to-one or one-to-many.

Cons: still issues multiple queries.

4) Subselect fetching (@Fetch(FetchMode.SUBSELECT))

  • Loads collections for a list of parent entities in a single subselect query:
@OneToMany(mappedBy = "author", fetch = FetchType.LAZY)
@Fetch(FetchMode.SUBSELECT)
private List<Book> books;

Pros: single secondary query; avoids Cartesian explosion.

Cons: large parent lists can produce large IN (...) clauses.

5) Adjust fetch type to EAGER (with caution)

  • Eager associations load automatically with the parent entity.
  • Example:
@OneToMany(fetch = FetchType.EAGER)
private List<Book> books;

Pros: eliminates lazy loading delays.

Cons: dangerous — can cause unexpected massive joins or performance hits if used blindly.

6) Manual queries with WHERE IN (...)

  • Write explicit queries to load all needed associations in one go.
  • Example:
SELECT b FROM Book b WHERE b.author.id IN :authorIds

Pros: complete control.

Cons: manual; increases boilerplate.

7) Hibernate second-level cache

  • Does not solve N+1 itself but mitigates its cost by avoiding repeated DB hits for the same entities.
  • Example:
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)

Pros: useful for read-heavy apps.

Cons: adds complexity; not a direct solution.

🔥 Key takeaway:

The best way to avoid N+1 in most cases is to either use JOIN FETCH, EntityGraph, or batch/subselect fetching — depending on the data shape, query, and your tolerance for Cartesian products or large IN clauses.

This entry was posted in Без рубрики. Bookmark the permalink.

Leave a Reply

Your email address will not be published.