skip to content

In a mapper, what changes at read and write time when a field holds a related row's key value instead of a mapped reference?

level: middleimportance: should knowfreq 48%

answer

  1. same column, different services
  2. a value field cannot navigate or cascade
  3. linking need not load the target
  4. one writable mapping per column
  5. the hidden load is the real cost

basics

~20 s

The stored column is identical; the services differ. A key value is an ordinary field with no navigation, no deferred loading and no cascade. A mapped reference buys those, at the cost of loads that fire behind a field access.

solid answer

~50 s

Both spellings write the same column. With a plain **key value**, the field is a value like any other: assigning it writes the column, reading the target is an explicit query you write, and nothing cascades. With a **mapped reference**, the layer knows the field is a link, so it can hand back a stand-in that loads on demand, include the target in a fetch plan, propagate declared cascades, and return one instance per row inside the unit of work. Neither spelling makes the link valid — that is the schema's foreign key. Setting a link does not require loading the target either way: most layers can produce an unloaded stand-in for a known key. If you map the same column both ways for convenience, exactly one of the two mappings must be writable, or the flush has two candidate values for one column.

go deeper

for a junior

Know that both forms end up as the same column in storage. A key value is just data you can set and filter on; a reference is a field the layer may fill in for you by running a query when you touch it.

for a middle

Explain what the layer stops doing for a value field — no deferred stand-in, no cascade, no one-instance-per-row guarantee — and that setting a link does not require loading the target under either mapping.

for a senior

Argue the choice from cost predictability: with a value, every load is a query you can see; with a reference, a field access can fire one at a moment nobody chose. Know the rule that only one mapping of a column may be writable.

for a principal

Use the mapping to fix boundaries. A key-only link keeps one subsystem's graph from being walked into another's, which is a structural constraint the type system enforces rather than a convention a reviewer has to police.

A link to another row can be mapped in two quite different ways. The field can hold the **key value** itself — a plain column, mapped like any number or string — or it can hold a **mapped reference** to the other object, so the layer knows the field means a link and does link things with it. The stored column is identical in both cases. Everything else differs. ## What the layer does for each | | Key value in a plain field | Mapped reference | |---|---|---| | Writing the link | assign the value; the column is written like any other | assign an object; the layer writes that object's key into the column | | Reading the target | an explicit query, written by the code | navigate the field; the layer loads on demand or as part of the fetch plan | | Cost of setting a link | none beyond the write | none, if the object is already at hand or an unloaded stand-in is used | | Deferred loading | not applicable — there is nothing to defer | the field may hold a stand-in that loads on first real use | | Cascade of saves or deletes | not available; the field is a value | available, because the layer can see the link | | Same-object guarantee | none; you get whatever a later query returns | the layer's identity map returns one instance per row inside a unit of work | | Integrity of the value | whatever the schema enforces | whatever the schema enforces; the layer does not vouch for it either | The last row surprises people. A mapped reference does not make the link safe. If the row it names is deleted by another path, both mappings are equally stale; the foreign-key constraint in the schema is what actually prevents a dangling value, and it protects the plain column exactly as well. ## Linking without loading The most common mistake with mapped references is believing that setting a link requires loading the other object. It usually does not. Most tracking layers can produce an **unloaded stand-in for a known key** — an object that carries the key and fetches the rest only if something touches another field. Assigning that stand-in writes the correct column with no `SELECT`. The plain key field reaches the same place by a shorter road: assign the value. Either way, `INSERT INTO child (parent_id, ...) VALUES (?, ...)` is one statement, and a candidate who insists on loading the parent first to "make the link valid" is buying a query that proves nothing, since the row can vanish between the read and the write anyway. ## Mapping the same column twice Teams often want both spellings: the reference for navigation, the value for filtering and for cheap writes. Most layers allow it, with one rule — **only one of the two may be writable**. If both are writable and the code sets them to different things, the flush has two candidate values for one column and the result depends on the layer's internal ordering. The usual arrangement is a writable reference plus a read-only key field, or a writable key field plus a read-only reference. Silent disagreement between two writable mappings for one column is a genuinely hard bug to find, because each half of the code is locally correct. ## Choosing between them Reach for the **mapped reference** when: - the code naturally walks the graph, and readable navigation is worth the mapping; - you want the layer's cascade and its one-instance-per-row guarantee for the target; - the target is small and usually needed anyway. Reach for the **key value** when: - the link is crossing a boundary you do not want the object graph to cross, and an accidental navigation would drag in a whole subsystem's data; - the write path only ever needs the value, and every navigation would be an unwanted load; - the target lives in a different store or is not mapped at all, so there is no object to reference; - you want writes and filters to be obviously one statement, with no possibility of a hidden load behind a field access. ## The read-path consequence Filtering on a link is the same `WHERE parent_id = ?` under both mappings, so this is not a query-performance choice. The difference is what an ordinary field access can cost. With a plain value, reading the field is free and getting the target is a visible, deliberate query. With a reference, reading the field is free too, but touching the target's other fields may fire a statement at a moment nobody chose — the cost moves from the code you can see to the code you cannot. That predictability, not speed, is the real argument for the plain value; navigation and cascade are the real arguments against it.

  • Does using a mapped reference guarantee the link points at a row that exists?
    No. The layer writes whatever key the reference carries, and the row can be deleted by another path immediately afterwards. The foreign-key constraint in the schema is what actually rejects a dangling value, and it protects a plain key column exactly as well.
  • How do you set a link when you have only the key and do not want to load the target?
    Assign the key value if the field is mapped that way, or obtain an unloaded stand-in for that key if it is mapped as a reference. Both write the column in one statement. Loading the full target first buys a query that proves nothing, since the row can vanish before the write lands.
  • What breaks if both the reference and the key column are mapped as writable?
    The flush has two sources for one column. If the code sets them to different values the outcome depends on the layer's internal ordering rather than on anything in the code. The usual arrangement is one writable mapping and one read-only mapping of the same column.

saying these in an interview costs you the question

  • Thinks linking two rows always requires loading the target first
  • Expects navigation or cascade from a plain key value field
  • Maps one column twice as writable and expects the two to agree
  • Believes a mapped reference validates that the row exists
  • Assumes a reference always costs an extra query to assign