Skip to content

dev48v/n-plus-one-visualizer

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 

Repository files navigation

🔢 N+1 Query Visualizer

Watch JPA/Hibernate's quietest performance killer happen. Load a list of N rows, touch a lazy association, and it fires one more query per row — 1 + N. Then fix it with JOIN FETCH, @EntityGraph, or batch fetching and watch the query count collapse.

▶ Live demo  ·  The fixes  ·  Run locally

License: MIT No dependencies JPA / Hibernate


Why

N+1 is the bug that doesn't look like a bug. With 5 rows in dev it's instant; in production with 500 rows it's 501 queries and a page that takes seconds. It never errors, so it hides until it hurts. Seeing the query log fill one line per row makes the problem — and the fix — obvious.

What it shows

Pick N and a strategy, hit run, and watch every SQL statement appear with a live query count and total round-trip time:

  • Lazy (default)SELECT * FROM orders (1) plus SELECT * FROM customer WHERE id=? once per order (N). That's the N+1: 1 + N queries for a single list.

The fixes

  • JOIN FETCHSELECT o, c FROM Order o JOIN FETCH o.customer1 query, customers joined in.
  • @EntityGraph@EntityGraph(attributePaths = "customer") on the repository method → the same single JOIN, declaratively, no custom JPQL.
  • Batch fetching@BatchSize(5) (or hibernate.default_batch_fetch_size) keeps lazy loading but groups the child selects into WHERE id IN (?,?,?,?,?) chunks → 1 + ⌈N/5⌉ queries. Great when a full join would multiply rows across several collections.

The "vs lazy" stat shows exactly how many round-trips each fix saves.

The gotcha

JOIN FETCH across two collections causes a cartesian product (rows multiply) — that's precisely when batch fetching wins. And FETCH + pagination (setMaxResults) makes Hibernate paginate in memory. There's no one right answer; this lets you feel the trade-off.

Run it locally

open index.html          # macOS
start index.html         # Windows
python -m http.server 8000   # or serve → http://localhost:8000

Note

A teaching model — the query counts and strategies match real Hibernate behaviour; timings are simulated. Turn on spring.jpa.show-sql=true (or hibernate.generate_statistics) in a real app to catch N+1 in your own logs.

Contributing

Ideas: nested collections (cartesian product demo), @Fetch(SUBSELECT), DTO projections, pagination-with-fetch pitfall. PRs welcome.

If this saved you from an N+1 in prod, a ⭐ helps others find it.

License

MIT © dev48v

About

Watch the JPA/Hibernate N+1 query problem happen (1+N) then fix it with JOIN FETCH / @EntityGraph / batch fetching. Live query count, zero deps.

Topics

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages