Part 1 · 2 chapters · ~12 min
The Core Machinery
Metadata and schema mapping, the identity map, the unit of work and ordered flush, dirty checking by snapshot, proxy or explicit marking, lazy loading and proxy objects, the N+1 problem and every fix, eager loading strategies and the row-explosion trade-off, cascades, and first- and second-level caches.
3
Sessions, identity and units of work
The machinery is the same in Hibernate, SQLAlchemy, MikroORM and Entity Framework: a session that guarantees one object per row, tracks what changed, and writes it back in a sensible order.
IDENTITY MAP AND UNIT OF WORK
one session: each row loaded once, changes tracked, flushed together in order
swipe the figure sideways, or tap expand for full screen
1/4
identity map
The session keeps one object per primary key. The first find queries the database; the second returns the same object from the map, so there is never more than one in-memory copy of a row per session.
one object per row per sessionrepeated finds hit the map
4
Lazy loading and N+1
code
// lazy: 101 queries
for (const t of await em.find(Transfer, {}, { limit: 50 })) console.log(t.account.owner.name);
// separate queries: 3
const ts = await em.find(Transfer, {}, { limit: 50, populate: ['account.owner'], strategy: 'select-in' });
// the same idea everywhere:
// Django select_related / prefetch_related Rails includes / preload / eager_load
// Hibernate JOIN FETCH / @BatchSize / entity graphs SQLAlchemy joinedload / selectinload
// Prisma include (and relationJoins) Drizzle db.query with `with`| machinery | production gotcha |
|---|---|
| cascades (persist, remove) | deleting a parent silently deletes thousands of children, or loads them all first |
| second-level cache | stale data across services that write the same tables directly |
| proxies | accessing a lazy association after the session closed (LazyInitializationException) |
| open-session-in-view | lazy loads from templates run queries during rendering; disable it (Java course P6) |
THE N+1 PROBLEM, BY LOADING STRATEGY
queries to show 50 transfers with account and owner
swipe the figure sideways, or tap expand for full screen
1/4
lazy loading
Each transfer's account is a lazy proxy; touching it runs a query, and so does its owner. 50 rows become 101 queries: N+1 (here 2N+1).
a query per row per association101 queries for 50 rows