In an object-relational mapper, what does it mean for a link to be eagerly fetched, and when does that data arrive?
answer
- about when, not how many
- loaded with the owner, not on touch
- declared on the mapping or per query
- join now or a second statement now
- every read of the owner pays
basics
~20 sEager means the linked data is loaded as part of the read that returns its owner - in the same statement through a join, or in an immediate follow-up statement - so nothing has to load later when code touches the link.
solid answer
~50 sEager fetching is a statement about **timing**: the linked object or collection is materialised at the moment the owner is read, not at the moment code first touches the reference. The layer may satisfy that either by joining the linked table into the same statement or by issuing an immediate secondary statement keyed by the identifiers it just read - both count as eager, so "eager" does not mean "one statement". Where it is declared matters as much as what it does: a fetch type written on the mapping is the link's default for *every* read of the owner, including a plain load by identifier, while a fetch named inside one query applies to that query only. Because the data is already in memory, the link stays readable after the unit of work closes; the price is that every read of the owner carries those bytes, whether the use case needs them or not.
go deeper
Recall the one-line definition: eager means loaded together with the owner, deferred means loaded when first touched. Be able to say that the timing, not the statement count, is what the word fixes.
Explain the two declaration sites - the link's mapping default versus a fetch named in one query - and that the layer may satisfy an eager link with a join or with an immediate secondary statement.
Show that you check what a read actually materialised rather than trusting the declaration: statements emitted, objects hydrated, bytes returned against the size of the response the endpoint sends.
Frame it as a defaults question. A mapping default is a global commitment every future read inherits; a per-query fetch is a local one. Say which direction you want the team's mistakes to fall in.
## What "eager" names A mapping layer turns rows into connected objects. Every link between mapped objects - the reference from an order to the customer that placed it, the collection of lines beneath it - has to be materialised at *some* moment, and the layer records which moment that is. - **Eager**: the moment the owner is read. By the time the read hands the object back, the linked data is already in memory. - **Deferred**: the moment application code first touches the link. So eager is a claim about **timing**, not about caching, not about indexing, and not about how many statements run. Two consequences follow at once: 1. An eagerly fetched link is readable after the unit of work that loaded it has ended, because nothing has to go back to the database. 2. Every read that returns the owner pays for the link - including the reads whose use case never looks at it. ## Where the decision is written down There are two declaration sites, and they have very different blast radii. | Declaration site | Applies to | Typical wording | |---|---|---| | The mapping | every read of the owner, including a load by identifier the layer issues on its own | "this link's default fetch type is eager" | | A single query | that query only | "fetch this link along with the result" | | A named plan attached to a call | wherever the plan is attached | "read the owner under the *invoice* plan" | A junior answer only has to know that both sites exist and that the mapping one is global. The mapping default is the one that surprises people, because it fires for reads nobody wrote a query for. ## How the data actually arrives Declaring a link eager tells the layer *when*, and leaves *how* to the layer: | Mechanism | Statement shape | Consequence | |---|---|---| | Join into the same statement | one statement, wider rows | the owner's columns repeat once per linked row when the link is a collection | | Immediate secondary statement | two or more statements, issued back to back | narrower rows, more round trips | | A mix, driven by a fetch plan | one statement per branch of the plan | the shape depends on which links the plan names | Both of the first two satisfy the contract of eager loading: nothing faults in later. This is why "we made it eager, so now it is one query" is an unsafe assumption - a layer is free to fetch an eager link with a second statement, and often does when joining would multiply rows. ## Defaults differ by link kind, and by layer No single default is universal, but the tendencies are stable enough to reason about: | Link kind | Common default | Reason | |---|---|---| | Single-valued reference (many-to-one) | frequently eager | at most one extra row joins in, so the cost looks bounded | | Collection (one-to-many, many-to-many) | usually deferred | the size is unbounded and unknown at mapping time | | Large text or binary value | often fetched only on request, by handle | the payload dwarfs the rest of the row | And the whole question is layer-dependent. A full mapper with a tracked set has to choose a default because it owns the object graph. A query builder or thin driver has no defaults to choose: there are no links to fault in, only the rows the query asked for, so every join is written by hand. Saying "it depends on the layer, and here is the tendency" is a better answer than asserting one rule. ## Why timing is the property that matters Timing determines three practical things: - **Where failures can occur.** A deferred link can fail or fault in far from the query - during rendering, serialisation, or on a thread with no open unit of work. An eager link cannot, because it is already there. - **What the statement log looks like.** Eager links are visible at read time, which makes them easy to see and easy to forget. - **Who pays.** The read pays, once, up front - even when the caller wanted one field. ## What it costs, and how to check The cost of eager loading is measured in **bytes and objects**, not in statements: rows returned, columns per row, objects hydrated into the tracked set, heap held until the unit of work ends. A statement counter shows a flat, healthy-looking number while the payload quietly triples. To see what a read really did: log the statements the layer emitted for one call, count the objects materialised against the number the response actually uses, and compare the bytes returned with the size of the rendered response. When those diverge, an eager default is usually the reason.
- Does eager fetching always collapse the read into a single statement?No. The declaration fixes the timing, not the statement shape. A layer may join the link into the same statement or fire an immediate secondary statement keyed by the identifiers it just read, and it may pick differently for a reference than for a collection. Both are eager, because neither leaves anything to fault in later.
- If a link is eager, can a later access to the object still hit the database?Not for that link on that object - it is already populated. Anything the plan did not cover still behaves normally: links beyond the fetched ones remain deferred and will fault in, or fail, depending on whether a unit of work is still open when they are touched.
- Is a mapping-level eager default the same as caching the link?No. A cache answers repeat reads from memory across calls; an eager default changes what a single read materialises. An eager link is fetched afresh on every read of the owner unless a shared cache separately serves it, which is a different mechanism with different invalidation concerns.
Eager loading is packing the whole toolbox before leaving the workshop; deferred loading is walking back for each tool. The toolbox never leaves you stranded - you just carry it everywhere, including to jobs that need one screwdriver.
saying these in an interview costs you the question
- Thinks eager means the linked data is cached rather than loaded up front.
- Says eager loading always produces a single joined statement.
- Believes every link is eager by default unless the mapping says otherwise.
- Cannot say when the load happens, only that eager is 'faster'.
- Assumes a mapping-level eager default applies only to hand-written queries.