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
applicationsessionidentity mapdatabasefind(Account, 7)SELECT ... WHERE id = 7store Account#7 + snapshotfind(Account, 7) againsame object, no query
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`
machineryproduction gotcha
cascades (persist, remove)deleting a parent silently deletes thousands of children, or loads them all first
second-level cachestale data across services that write the same tables directly
proxiesaccessing a lazy association after the session closed (LazyInitializationException)
open-session-in-viewlazy 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
lazy loading in a loop1 + 50 + 50 = 101eager: JOIN1 (wide rows)eager: separate IN queries3DataLoader batching3 (per tick)
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