Describe what EntityManager.detach(entity), EntityManager.clear() and closing the EntityManager each do to the lifecycle state of the entities involved, and what happens to changes made to those entities that had not been flushed yet.
answer
- detach = one, clear = all + action queue, close = the whole manager
- evicted ⇒ detached ⇒ no snapshot ⇒ no dirty check
- unflushed changes disappear silently
- flush() BEFORE clear(), never after
- clear() cancels pending persists; detach() is not an undo
basics
~20 sAll three move managed instances to the detached state: detach() one instance, clear() every instance, close() ends the context entirely. Unflushed changes on those instances are silently discarded — no dirty check can run on an entity the context no longer tracks.
solid answer
~40 s`detach(entity)` evicts a single instance from the persistence context; `clear()` evicts all of them and drops the pending action queue; `close()` ends the EntityManager altogether. In every case the affected instances become **detached** — they keep their ids and field values, but Hibernate no longer holds a snapshot for them, so nothing they do afterwards produces SQL. Crucially, changes made *before* the eviction but not yet flushed are lost **silently**: dirty checking compares a managed instance against its snapshot at flush, and after eviction there is neither. If you want those changes, call `flush()` before detaching. `clear()` also discards queued work, so entities you `persist()`ed but never flushed are not inserted. This is why the flush-then-clear pair is the standard batching idiom: `flush()` pushes the SQL out, `clear()` frees the memory.
code
java · 6 linesorder.setStatus(SHIPPED);
em.detach(order); // change silently lost - no UPDATE ever
order2.setStatus(SHIPPED);
em.flush(); // UPDATE emitted
em.detach(order2); // now safego deeper
Say all three produce detached entities and that unflushed changes are lost, and remember flush-before-clear.
Distinguish the scopes precisely, explain the snapshot mechanism that makes the loss silent, and state that clear() also drops queued actions.
Use flush+clear as the standard batching idiom, know that per-instance detach is not an undo for a queued insert, and contrast detach with read-only entities.
Position context lifetime as an architectural choice — how long a unit of work holds entities, where detachment marks the layer boundary, and whether DTO mapping is the better boundary tool.
## The three eviction operations They differ only in scope. **`em.detach(entity)`** removes one instance from the persistence context. Hibernate's native equivalent is `session.evict(entity)`. Everything else in the context stays managed. **`em.clear()`** removes *all* managed instances and resets the context to empty. It also throws away the queued actions that have not been flushed. **`em.close()`** releases the EntityManager entirely. The context is gone, and any further use of that EntityManager throws `IllegalStateException`. Entities you already hold references to survive as detached objects. A fourth, implicit route exists: with a transaction-scoped EntityManager (the container-managed style), everything becomes detached when the transaction ends, without any explicit call. ## Why unflushed changes vanish silently When Hibernate loads or persists an entity, it stores two things: the instance itself in an identity map, and a **snapshot** — an array of the property values as of the last synchronisation with the database. Dirty checking at flush is nothing more than comparing the live object to that snapshot and emitting an UPDATE for the differences. Eviction discards both the entry and the snapshot. There is no longer anything to compare, and no registration to iterate over at flush. So a modification made a line before `detach()` disappears without a warning, an exception, or a log line at default levels. This is one of the most-reported "my update didn't save" causes, and the fix is a discipline: **flush before you evict** if the changes matter. ``` entity.setStatus(SHIPPED); em.flush(); // UPDATE goes out now em.detach(entity); ``` ## clear() also drops queued work `clear()` is stronger than "detach everything": it also empties the action queue. Entities passed to `persist()` but not yet flushed are therefore never inserted, and pending deletes are not executed. That is intended — `clear()` means "forget this unit of work". Per-instance `detach()` is subtler and worth flagging as a trap: it removes the entity from the context, but a queued insert action for that entity may already exist in the action queue and can still fire at flush. Do not treat `detach()` as an "undo persist". If you need to abandon pending work, `clear()` (or rolling the transaction back) is the honest tool. ## What a detached instance can and cannot do After eviction the instance keeps its identifier and whatever data was already loaded. What it loses: - **Automatic write-behind**: setters produce nothing. - **Lazy loading**: uninitialised proxies and collections can no longer fetch, because the session that would fetch them is gone or no longer owns them. - **Identity guarantees**: a later `find()` for the same id in the same (or a new) context creates a *different* instance, so `==` comparisons between the detached copy and the freshly loaded one fail. Entities that will be compared across contexts need an `equals`/`hashCode` based on a stable business key rather than a generated id. What it keeps: its data, so it is perfectly good as a read-only carrier — which is why detaching deliberately is sometimes the point, not an accident. ## The flush + clear batching idiom The canonical use of `clear()` is bounding memory in a long unit of work: ``` for (int i = 0; i < rows.size(); i++) { em.persist(map(rows.get(i))); if (i % 50 == 0) { em.flush(); em.clear(); } } ``` `flush()` writes the batch, `clear()` releases the instances and their snapshots so the heap stays flat and dirty checking does not degrade as the context grows. Getting the order wrong — `clear()` before `flush()` — throws the batch away. ## Deliberate detachment as a design tool Sometimes you evict on purpose: you want to hand an object to code that must not accidentally write to the database, or you are about to mutate a loaded entity for presentation only. `em.detach(x)` makes that safe and explicit. Compare it with the alternatives — mapping to a DTO (clearer, more code) or marking the entity read-only via Hibernate's `Session.setReadOnly` (keeps it managed and lazily loadable, but skips dirty checking). Choosing between those three is a real design decision, not a matter of taste: detachment removes lazy loading, read-only keeps it. ## Quick diagnosis table - Change lost with no error → something evicted the instance before flush. - `IllegalStateException: EntityManager is closed` → you kept using the manager after `close()`. - Insert happened even though you "cancelled" it with `detach()` → queued action still in the queue; use `clear()` or rollback. - Heap climbing through a long loop → you flushed but never cleared.
- After em.clear(), you call em.find() for an entity you had already loaded. What do you get?A brand-new SELECT and a brand-new instance. The identity map was emptied, so nothing can be served from memory, and the object you still hold from before is detached and unrelated to the new one. The two are not `==`, and if your equals/hashCode is identity-based they will not even be equal, which is what breaks sets and maps built across a clear().
- You want a loaded entity to be safe from accidental updates but still able to load its lazy associations. Is detach() the right call?No — detaching removes lazy loading along with dirty checking. Hibernate's `session.setReadOnly(entity, true)` (or a read-only query hint) keeps the entity managed and lazily loadable but drops its dirty-check snapshot, so no UPDATE can be emitted for it. That is the tool for read-only-but-live; detach is the tool for handing an object out of the persistence layer entirely.
saying these in an interview costs you the question
- Believing detach/clear flush pending changes for you before evicting.
- Calling clear() and then expecting the batch you just persisted to have been written.
- Treating detach() as a way to cancel a pending persist.
- Assuming close() throws away the entity objects themselves rather than leaving them detached.
- Expecting == to still hold between a detached instance and a freshly loaded one for the same row.