Skip to main content

Performance

Performance — performance optimization, benchmarking, and profiling in Java applications

Lazy Loading in Hibernate 7: Three Fetch Strategies, Benchmarked, with the One Most Tutorials Get Wrong

Switching from EAGER to LAZY on a @ManyToOne association made one of our REST endpoints 4x slower. That's the opposite of what every Hibernate tutorial promises, and understanding why it happened is more useful than any fetch-type cheatsheet. The endpoint served an order detail page. The Order had a @ManyToOne Customer, which was previously EAGER. Response time was ~80ms. We switched to LAZY (correctly, as a "best practice") and response time jumped to ~320ms. The reason: the EAGER mapping had been loading Customer in the same query as Order via a JOIN. LAZY replaced that with a second SELECT — and the second SELECT ran after the session had been handed to Jackson for serialisation, which triggered OSIV to keep the connection open, added latency from the extra round-trip, and eventually started failing under load when connection pool slots ran out waiting for the serialisation thread to finish. LAZY was still the right answer. The fix was a proper fetch strategy. But "switch everything to LAZY" without understanding the four fetch options and when each one earns its keep is how you trade one performance problem for a different one.

Mastering Hibernate 7 @Immutable Entities: Performance, Safety, and Best Practices

@Immutable is a pure marker annotation that skips Hibernate's dirty-checking snapshot -- mutate a field and flush, and nothing happens: no UPDATE, no exception, just silent data loss of the in-memory change. This rewrite runs every claim against Hibernate 7.4.5 -- entity mutation, bulk HQL (rejected at translation time by default, a correction neither prior version made), collection mutation, @Immutable+@Version, and a real flush-cost measurement -- and compares the annotation head-to-head with Session.setReadOnly().

Hibernate 7 Natural IDs: What L1 and L2 Caching Actually Give You, Measured

Verified against Hibernate 7.4.5: a repeat bySimpleNaturalId lookup in the same session costs zero extra queries with no cache at all, but the bigger correction is that @NaturalIdCache populates its L2 region at insert time, not on first lookup -- so the textbook miss-then-hit story only applies to rows Hibernate didn't insert itself.