How Hibernate Works
IntermediateHibernate is an object-relational mapping (ORM) framework: it maps your Java objects to database tables so you work with objects instead of SQL. Behind the scenes it maintains a persistence context — a per-transaction cache of managed entities — tracks changes via dirty checking, and flushes the right SQL at the right time. Its power (lazy loading, caching, automatic updates) is also its danger: misused, it causes the infamous N+1 query problem.
Think of Hibernate as a smart personal assistant
You tell your assistant "change this client address" by editing a note on your desk (the entity). You never write the paperwork. The assistant remembers every note you touched (the persistence context), notices which changed (dirty checking), and files the exact updates with head office (the database) at the end of the day (flush). If you keep asking for related documents one at a time, though, the assistant makes a separate trip for each — the N+1 problem.
Step by Step
Key Concepts
Persistence Context (First-Level Cache)
A per-transaction map of managed entities. It guarantees one instance per row, enables dirty checking, and avoids duplicate reads within the same transaction.
N+1 Query Problem
Fetching a list of N parents, then lazily loading each parent children in a separate query — 1 + N queries. Fix it with a JOIN FETCH, an entity graph, or batch fetching.
Lazy vs Eager Loading
Lazy defers loading an association until accessed (default for collections); eager loads it immediately with the parent. Eager on many associations creates huge, slow joins — prefer lazy plus explicit fetching.
Entity Lifecycle
Entities move through states: transient (new, unmanaged), managed (tracked in the context), detached (was managed, context closed), and removed (scheduled for delete).
Key Facts
- You do not call save() to persist a change to a managed entity — dirty checking updates it automatically at flush time.
- LazyInitializationException means you accessed a lazy association after the transaction/session closed; fetch it inside the transaction or use a fetch join.
- The N+1 problem is the single most common Hibernate performance bug — always check the SQL log when a list endpoint is slow.
Real-World Applications
Fixing a slow list endpoint
A page listing orders with their customer fires one query per order for the customer (N+1). Switching to SELECT o FROM Order o JOIN FETCH o.customer loads everything in a single query, often a 10x speedup.
Auditing without extra code
Because dirty checking tracks changes, Hibernate interceptors or Envers can record who changed what and when, giving audit history without touching business logic.
Frequently Asked Questions
What is the N+1 query problem and how do I fix it?
It happens when you load N parent entities, then lazily load a child association for each one — resulting in 1 + N queries. Fix it by fetching the association eagerly for that query with a JOIN FETCH in JPQL, an @EntityGraph, or Hibernate batch fetching (@BatchSize).
What is the persistence context?
It is Hibernate first-level cache — a per-transaction set of managed entities. It ensures the same database row maps to one object instance, enables automatic dirty-checking updates, and avoids repeated reads of the same entity within a transaction.
Why do I get LazyInitializationException?
You accessed a lazily-loaded association after the Hibernate session/transaction closed, so Hibernate can no longer fetch it. Load the data while the transaction is open (JOIN FETCH, entity graph) or restructure so the association is accessed inside the transactional boundary.
Do I need to call save() to update an entity?
No. If the entity is managed (loaded within the current transaction), simply changing its fields is enough — Hibernate detects the change via dirty checking and issues the UPDATE at flush. You call save/persist only for new (transient) entities.