skip to content

Data Access & ORM Concepts

The ideas under every data-access layer: rows becoming objects that keep identity, links that load late, edits flushed at a boundary, schema change, cached copies. Asked because data bugs start here.

on this pageshow

explore

questions

219 · 8 sections

In a data-access layer that maps a link from both classes, what does it mean that one end owns the write?

level: juniorimportance: must knowfreq 72%
basics
~20 s

The owning end is the side whose state the mapper turns into the foreign-key or junction-row statement. The other end is mapped for reading and navigation: changing it alone normally produces no SQL at all.

open as a page

In a data-access layer, what is an always-on filter on a mapped type, and which reads does it affect?

level: juniorimportance: must knowfreq 62%
basics
~20 s

An always-on filter is a predicate declared once on a mapped type, such as live-rows-only or current-tenant-only, that the layer adds to the WHERE clause of the statements it generates for that type, so no caller has to restate it.

open as a page

When the store assigns a key during the insert, at what point does a mapped object learn its identifier?

level: juniorimportance: must knowfreq 72%
basics
~20 s

Only when the insert actually runs. Until that statement reaches the store the key field is empty, so asking a pending object for its identifier forces the layer to write that row immediately and read the generated value back.

open as a page

When a data-access layer maps a class hierarchy, how does it decide which concrete subclass to instantiate for a row?

level: juniorimportance: must knowfreq 58%
basics
~20 s

Either a type-marker column stored with the row names the class, or the layer discovers which subtype table holds a row for that key. The marker is free with the read; discovery costs a join or a probe per subtype.

open as a page

Where can a data-access layer's mapping rules live, and what does convention-only mapping mean?

level: juniorimportance: must knowfreq 62%
basics
~20 s

Mapping rules have four possible homes: on the class itself, in an external configuration document, in a startup configuration API, or nowhere — derived by convention from class and property names. Most layers mix conventions with targeted overrides.

open as a page

What does it mean for a data-access layer to fill deferred links in identifier batches rather than one at a time?

level: juniorimportance: must knowfreq 52%
basics
~20 s

Rather than one statement per touched stand-in, the layer gathers the keys of other unfilled stand-ins of the same kind and fetches them together with a set-membership predicate, turning N secondary statements into roughly N divided by the batch size.

open as a page

When one statement fetches parents together with their child collection, why does the same parent come back repeatedly?

level: juniorimportance: must knowfreq 68%
basics
~20 s

The join returns one row per matching child, with the parent's columns repeated on every one of them. A layer that produces one result entry per row therefore hands back the same parent once per child row.

open as a page

When a data-access layer returns a deferred stand-in for a related object, which accesses fire a load and which do not?

level: juniorimportance: must knowfreq 70%
basics
~20 s

Any access that needs data the row has not supplied fires the load: reading a mapped field, iterating or sizing a collection, or calling a behaviour method on the stand-in. Reading the identifier it already holds does not.

open as a page

What happens when code touches a not-yet-loaded association after the unit of work that loaded the object has closed?

level: juniorimportance: must knowfreq 74%
basics
~20 s

The read fails at that moment. A deferred link still needs its own query to fill itself, and the unit of work and connection that would run it are gone, so most layers raise an error instead of returning data.

open as a page

When a data-access layer serves one request, what does counting its SQL statements reveal that the request's duration does not?

level: juniorimportance: must knowfreq 70%
basics
~20 s

A statement count exposes the shape of the access pattern - one statement per row versus a fixed few - independently of machine speed or data volume. Duration says only that something was slow, once it already is.

open as a page

In a data-access layer, what does batching accumulated writes mean, and why is it faster than one statement per row?

level: juniorimportance: must knowfreq 62%
basics
~10 s

Batching sends many same-shaped statements in one round trip, one parameter set per row, instead of a request-and-wait per row. The database still does each row's work; what disappears is the per-row waiting.

open as a page

When a list read fires one query per row, what remedies remove the repeated queries?

level: juniorimportance: must knowfreq 72%
basics
~20 s

Four families: pull the link into the parents' statement with a join fetch, load the whole page's links in one extra bulk statement, project the read down to the fields it needs, or serve the link from a cache.

open as a page

In a layer that loads linked data on first touch, which line of code emits the extra per-row statements?

level: juniorimportance: must knowfreq 72%
basics
~10 s

The line that first touches a deferred link while iterating — an ordinary field read, not a query call. The list read is one statement; every touch inside the iteration adds another.

open as a page

In a data-access test, why can reading a just-saved object back inside the same unit of work pass without the database storing it?

level: juniorimportance: must knowfreq 64%
basics
~20 s

A mapper holds saved objects in an in-memory identity map and defers the write until a flush. A read by the same id hands back that instance, so the assertion passes whether or not a statement ever reached the database.

open as a page

When a data-access layer loads a row into a tracked object, what snapshot does it keep, and what is that snapshot for?

level: juniorimportance: must knowfreq 72%
basics
~20 s

A tracking layer copies each loaded row's column values into a private snapshot beside the object. At flush it compares the live object against that snapshot; fields that differ become the UPDATE, and an object with no difference produces none.

open as a page

In a data-access layer with a tracked set, what happens to an object's unwritten edits and unresolved links when it is detached?

level: juniorimportance: must knowfreq 72%
basics
~20 s

Detaching removes the object from change tracking: later edits are no longer noticed or written, edits not yet flushed are usually lost, and links left as unresolved stand-ins have no set behind them to load through.

open as a page

In a data-access layer that tracks loaded objects, what does a flush do, and how is it different from a commit?

level: juniorimportance: must knowfreq 82%
basics
~20 s

A flush turns the unit of work's accumulated in-memory changes into SQL statements sent on the already-open transaction. A commit ends that transaction, making the changes durable and visible to others. Flushed-but-uncommitted work is still discarded by a rollback.

open as a page

In a data-access layer with an identity map, what does loading the same row twice inside one tracked set give you?

level: juniorimportance: must knowfreq 68%
basics
~20 s

You get the same object both times. A tracked set's identity map, keyed by type plus identifier, makes one row exactly one object for the life of that set; a by-key lookup it already holds needs no statement.

open as a page

In a data-access layer that tracks loaded objects, what do the transient, managed, detached and removed states mean?

level: juniorimportance: must knowfreq 74%
basics
~20 s

Transient means never stored and not watched. Managed means the tracked set holds it and will write its edits. Detached means it once was managed but its set has ended. Removed means a delete is planned.

open as a page

In a data-access layer, what does an after-commit callback guarantee that code placed right after the last save does not?

level: juniorimportance: must knowfreq 70%
basics
~20 s

An after-commit callback runs only once the transaction has actually committed, so its side effect can never announce a change that later disappears. Code written in line after the last save still sits inside the open, undecided transaction.

open as a page

Why is a transaction boundary usually placed around one use case rather than around each repository call?

level: juniorimportance: must knowfreq 72%
basics
~20 s

A transaction commits or rolls back as a whole, so the boundary must enclose everything that has to be all-or-nothing - usually one use case. Per-call commits make each write permanent on its own, so a later failure leaves half-written data.

open as a page

Why does a data-access layer translate database errors into its own exception types instead of exposing vendor codes?

level: juniorimportance: must knowfreq 64%
basics
~20 s

A translation step maps engine-specific codes and messages onto a small portable set of failure kinds - constraint violation, deadlock victim, serialization failure, lock or statement timeout, lost connection - so calling code branches on the kind, not on a number.

open as a page

When a data-access layer loads an object with a pessimistic lock requested, what changes about the read, and how long does the lock last?

level: juniorimportance: must knowfreq 62%
basics
~20 s

The layer turns the read into a locking read, so the row is claimed for your transaction as it is fetched. Other writers of that row wait. The lock is released only when the transaction boundary ends.

open as a page

What does marking a unit of work read-only change about how a data-access layer handles the objects it loads?

level: juniorimportance: must knowfreq 60%
basics
~20 s

A read-only unit stops keeping a value snapshot of each loaded object, so no dirty check and no flush run at the end. Reads get a lighter, cheaper path, and anything modified inside the unit is simply not written.

open as a page

When a data-access layer builds the schema from its mapping, what do its re-create, auto-alter, validate-only and none modes do?

level: juniorimportance: must knowfreq 60%
basics
~20 s

Schema-from-mapping modes differ in how far the layer will change the database: re-create drops and rebuilds the mapped tables, auto-alter adds what is missing in place, validate-only compares and refuses to start on a mismatch, and none touches nothing.

open as a page

Why do teams replay the whole ordered chain of schema change units from an empty database in tests?

level: juniorimportance: must knowfreq 62%
basics
~20 s

Replaying every change unit from empty proves the chain itself still builds a valid schema, catching ordering and dependency errors that a developer's long-lived database hides. Running only the newest unit leaves the rest of the chain untested.

open as a page

How do you write a backfill or seed insert so that re-running it after an interruption is harmless and resumes?

level: middleimportance: must knowfreq 60%
basics
~20 s

Write the predicate so it selects only outstanding work, compute values absolutely from source columns rather than adding to the current one, guard inserts on a unique key, and commit progress so a restart continues where it stopped.

open as a page

When data must be moved alongside a schema change, what does writing it as SQL statements buy over running it through the mapping?

level: middleimportance: must knowfreq 64%
basics
~20 s

SQL statements run set-based against the schema as that point in history left it; no later code edit can alter them. Running the change through the mapping adds logic the database lacks, but ties a historical step to moving code.

open as a page

Why must a migration script generated by diffing the mapping against the last known schema be reviewed before it runs?

level: middleimportance: must knowfreq 55%
basics
~20 s

A diff compares two end states, not the edit that produced them, so it cannot see intent. A renamed field reaches the script as a dropped column plus an added one, which destroys the data unless a human rewrites it.

open as a page

In a data-access layer, what is a set-based update, and how does it differ from load-modify-flush?

level: juniorimportance: must knowfreq 60%
basics
~20 s

A set-based update sends one UPDATE ... WHERE statement to the database through the layer. Load-modify-flush instead reads matching rows into objects, changes them in memory, and lets the layer write each changed object back.

open as a page

When a search filter arrives empty, why should a composed query omit its predicate instead of adding an always-true condition?

level: juniorimportance: must knowfreq 66%
basics
~20 s

An absent filter should contribute no predicate at all. A constant-true pad is only a concatenation crutch, a self-comparison silently drops rows holding no value, and a parameter-driven catch-all emits a different statement whose one plan must serve every combination.

open as a page

In a data-access layer, what is a native statement, and what result shapes can it be mapped into?

level: juniorimportance: must knowfreq 72%
basics
~20 s

A native statement is SQL you write in the database's own language and hand to the layer unchanged, rather than an object-level query it translates. Its rows map back to entity objects, transfer models, or scalars.

open as a page

In a data-access layer, what is an object-level query string, and when are its mistakes caught?

level: juniorimportance: must knowfreq 72%
basics
~20 s

An object-level query is text naming mapped classes and fields, not tables and columns; the layer parses it and emits SQL. Being text, a typo or a renamed field surfaces only at parse time - startup or first run.

open as a page

In a data-access layer, what does it mean to project a query into a transfer type, tuple or scalar?

level: juniorimportance: must knowfreq 66%
basics
~20 s

A projection asks the statement for only the fields a use case needs and materialises them as a scalar, a tuple or a small transfer type, so the row is never turned into a tracked mapped object.

open as a page

In a data-access layer that caches loaded rows, what makes a cached entry stale, and which writes leave one behind?

level: juniorimportance: must knowfreq 70%
basics
~20 s

A cached entry goes stale when the row it copies changes and nothing removes the copy. Any write the caching layer never sees can leave one behind: a set-based statement, a native statement, another service, or an operator at a console.

open as a page

What is a data-access cache that outlives a unit of work and is keyed by identifier, and which read does it serve?

level: juniorimportance: must knowfreq 60%
basics
~20 s

A shared cache holds mapped row state by type and identifier outside any single unit of work, so a load by key that hits it returns an object with no statement sent. Filtered queries still reach the database.

open as a page

Where should a service read and populate an application cache, relative to its transaction?

level: middleimportance: must knowfreq 57%
basics
~20 s

Read before the unit of work opens, so a hit costs no connection or transaction. Populate only after the commit returns, never from inside the transaction or the mapper's flush path, so nothing that could still roll back is published.

open as a page

What does an application cache of transfer models give up that the mapper's own cache keeps?

level: middleimportance: must knowfreq 60%
basics
~20 s

Identity, deferred completion and change detection. A cached answer is a detached snapshot with nothing behind it, so no shared instance, no link that can still load, no dirty checking. In exchange it skips the whole assembly path and travels between processes.

open as a page

In a layer whose cache outlives one unit of work, why invalidate a row's entry both before the write and after the commit?

level: middleimportance: must knowfreq 62%
basics
~20 s

Removing before the write stops further hits on a copy about to become wrong; removing again after the commit drops the pre-image that a concurrent reader reloaded and put back during the write. Populate only after a real commit.

open as a page