Interview replay
Full round replay — dirty checking
Timed verbal replay with pass/fail criteria per follow-up.
How to run this
The opener
“What is dirty checking, and why did my entity save without a save() call?”
Budget: 45 seconds. Going long here is itself a fail signal.
Follow-ups
1. “What is it comparing against?”
Testing: Snapshot, not the database.
Scoring
Pass: An in-memory snapshot the persistence context took when it loaded the entity — a copy of every persistent field. Not the database; it does not re-read anything to find out what changed.
Fail: Says it queries the database to compare.
2. “When does the flush happen?”
Testing: The half people miss.
Scoring
Pass: At commit, and also before most queries, so a query sees pending changes. That means a read part-way through a method can trigger writes from earlier in it.
Fail: Only says at commit.
3. “What does save() do on an already-managed entity?”
Testing: Whether they know it is redundant.
Scoring
Pass: Nothing — the entity was going to be written at flush anyway. There is an argument for keeping it as documentation that the method writes, but it changes no behaviour.
Fail: Believes it is required.
4. “When does mutating an entity NOT write?”
Testing: The mirror case.
Scoring
Pass: When it is detached — returned from a service whose transaction ended, deserialised, or constructed — because there is no snapshot to compare against. And under readOnly = true, where the snapshot is never taken.
Fail: Thinks setters always persist.
5. “What does dirty checking cost?”
Testing: Whether they see it as free.
Scoring
Pass: Two copies of everything loaded, since the context holds the entity and its snapshot. And a flush proportional to what has been loaded rather than to what changed — fifty thousand entities means comparing all of their fields to find one change.
Fail: Assumes it is free.
6. “A read-only batch job runs out of memory. Why?”
Testing: Applying the cost.
Scoring
Pass: It accumulates. Every loaded entity plus its snapshot is retained until the transaction ends, so a job that walks a large table uses roughly twice the entities' footprint. flush() and clear() periodically, a StatelessSession, or readOnly to drop the snapshot half.
Fail: Blames the result set size alone.
7. “Someone fixes that by flushing more often. Does it help?”
Testing: The wrong fix that sounds right.
Scoring
Pass: No. flush() writes pending changes and keeps every entity managed, so memory is unchanged and each flush re-compares everything loaded so far — it gets slower. clear() is what releases them.
Fail: Accepts flush as the fix.
8. “What does readOnly = true actually change?”
Testing: Precision.
Scoring
Pass: Hibernate skips taking the snapshot, so there is no dirty checking and no flush. Real saving on memory and time, and any mutation in that method is then silently discarded with no error.
Fail: Calls it just a hint or just documentation.
9. “Which columns does the UPDATE set?”
Testing: A detail with real consequences.
Scoring
Pass: All of them by default — one statement per entity type, built at startup and cached. That is why column-level audit triggers fire for columns that did not change. @DynamicUpdate builds it per flush from the changed fields.
Fail: Assumes only the changed column.
10. “A method changes a field then returns early to avoid saving. Does it work?”
Testing: The most damaging misunderstanding.
Scoring
Pass: No. The entity is managed and mutated, so the transaction commits and the UPDATE is issued. Returning is not a rollback — validate before mutating, or throw so the transaction actually rolls back.
Fail: Believes the early return prevents the write.
Score yourself
← Back to What is dirty checking, and why did my entity save without a save() call?