In JPA/Hibernate, an entity instance is always in one of four lifecycle states with respect to a persistence context. Name those states and describe how an object moves from one to another.
answer
- transient / managed / detached / removed
- persist, find, merge, remove, clear, close
- managed = snapshot + auto UPDATE at flush
- merge returns a managed copy, argument stays detached
- unsaved-value heuristic decides new vs detached
basics
~20 sTransient (new, unknown to the context), managed (tracked by an open EntityManager, changes written automatically), detached (was managed, context ended), removed (scheduled for DELETE). persist, find/query, detach/clear/close, merge and remove move an object between them.
solid answer
~50 sAn entity object is **transient** when you just `new`ed it: no row, nobody tracking it. `persist()` makes it **managed** — it lives in the persistence context, Hibernate keeps a snapshot of its loaded values, and a plain setter is enough to produce an UPDATE at flush. Anything returned by `find()`, `getReference()` or a JPQL query is managed too. When the context ends — `detach(entity)`, `clear()`, `close()`, or the end of a transaction-scoped EntityManager — the instance becomes **detached**: it still holds data and its id, but changes to it are invisible to the database until you `merge()` it back, which returns a managed copy. `remove()` on a managed instance makes it **removed**: still in the context, still readable, but queued for a DELETE at flush; `persist()` on it cancels the removal. The state answers one question: will touching this object generate SQL?
code
java · 8 linesBook b = new Book("Dune"); // transient
em.persist(b); // managed (INSERT queued)
b.setTitle("Dune (rev.)"); // managed -> dirty, no explicit save needed
em.detach(b); // detached
b.setTitle("ignored"); // no SQL will ever be emitted for this
Book managed = em.merge(b); // managed copy; b is still detached
em.remove(managed); // removed: DELETE queued for next flush
em.persist(managed); // cancels the removal -> managed againgo deeper
Name the four states and one transition for each; be clear that a setter on a managed entity is enough to update the row.
Add the exact triggers (detach/clear/close, transaction end) and the merge-returns-a-copy rule, plus what happens to removed entities before flush.
Frame it as a debugging tool: 'which state is this instance in' explains almost every lost-update or duplicate-insert bug, and mention the unsaved-value heuristic.
Discuss where state boundaries fall in a system's layering — how far managed entities are allowed to travel, whether detached objects or DTOs cross the service boundary, and the cost of merge-heavy designs.
## What a persistence context is An `EntityManager` (Hibernate's `Session`) owns a *persistence context*: an in-memory map of the entity instances it currently manages, keyed by entity type plus primary key. Every JPA lifecycle rule is a statement about the relationship between a Java object and that map. The four "states" are not flags stored on your object — they describe whether the context knows about the instance and what it intends to do with it. ## The four states **Transient (JPA calls it *new*).** An object you created with `new`. No persistence context has ever seen it and no database row corresponds to it. Setting fields does nothing beyond changing a Java object. It typically has no identifier yet, or one you assigned by hand that Hibernate has no record of. **Managed (persistent).** The instance is registered in the persistence context of an *open* EntityManager and corresponds to a row that exists or will exist. When Hibernate loaded it, it also kept a *snapshot* — a copy of the column values as read. At flush time it compares the object against that snapshot and emits an UPDATE for whatever differs. There is no "save" call: a setter on a managed entity inside a transaction is a database write. **Detached.** The instance *was* managed, but its persistence context is gone: you called `detach(entity)` or `clear()`, the EntityManager was closed, the transaction that scoped it ended, or the object was serialised and sent elsewhere. It still carries its identifier and field values, but nothing is watching it. Mutations are invisible to the database, and lazy associations that were never initialised can no longer be loaded (that is where `LazyInitializationException` comes from). To get changes back in, you `merge()` it — which copies the state onto a managed instance and *returns* that instance; the object you passed in stays detached. **Removed.** You called `remove()` on a managed instance. It is still in the context and still readable in Java, but it is scheduled for deletion. The DELETE runs at the next flush (query-triggered, explicit, or at commit), not at the moment of the call. ## The transition map - transient → managed: `persist()` (or a cascade of PERSIST from a managed parent) - database row → managed: `find()`, `getReference()`, a JPQL/HQL/Criteria query, `refresh()` - managed → detached: `detach(entity)`, `clear()`, `close()`, end of a transaction-scoped EntityManager, serialisation - detached → managed: `merge()` (returns a *different*, managed instance); Hibernate-native `Session.update()`/`lock()` reattach the same instance - managed → removed: `remove()` (or cascade REMOVE / orphan removal) - removed → managed: `persist()` on a removed instance cancels the pending delete - removed → after commit the row is gone and the object is an ordinary Java object again Calling `remove()` on a *detached* instance is an error (`IllegalArgumentException`, or a failure at commit); calling it on a transient one is simply ignored. ## How Hibernate guesses a state it wasn't told When you hand Hibernate an object it does not have in the context — typically to `merge()` — it must decide whether that object is transient (INSERT) or detached (UPDATE). It uses the *unsaved-value* heuristic: a null or zero identifier means new; a null `@Version` field means new; a custom `Interceptor.isTransient` can override. This is why a detached object whose id you accidentally cleared gets INSERTed as a duplicate, and why entities with application-assigned identifiers need care. ## Why the state is the first thing to establish when debugging Almost every "my change didn't save" or "why did this INSERT twice" bug is a state question. Three practical consequences follow from the state alone: (1) does mutating this object produce SQL — only if managed; (2) can I navigate an uninitialised lazy association — only if managed and the context is open; (3) is this instance the canonical one for its row — only managed instances are guaranteed unique per id inside a context, so a detached copy and a managed instance of the same row can coexist and disagree. `EntityManager.contains(obj)` tells you whether *that exact instance* is managed. It is reference-based, not id-based, which is exactly what you want when you suspect you are holding a stale copy.
- You create an object, set its @Id yourself, and pass it to merge(). Will Hibernate INSERT or UPDATE?It depends on Hibernate's unsaved-value heuristic, not on your intent. With a non-null assigned identifier and no version field, Hibernate treats the object as detached, so it issues a SELECT to look for the row and then an UPDATE (or an INSERT if no row was found). Adding a `@Version` field makes the test reliable: a null version means new. For assigned identifiers this ambiguity is precisely why people prefer explicit `persist()` on genuinely new objects.
- A managed order references a Customer object that was never persisted. What happens at flush?Hibernate walks the associations of managed entities during flush and finds a reference to a transient instance. Unless the association is annotated with `cascade = PERSIST` (or ALL), it throws a `TransientObjectException` / `TransientPropertyValueException` rather than writing a row with a dangling foreign key. The fixes are to persist the referenced object first or to declare the cascade.
The persistence context is a librarian's desk. A book you wrote at home is transient. Hand it over and it is on the desk (managed) — the librarian notices every pencil mark you make and copies it into the catalogue at closing time. Take it home again (detached) and your scribbles are yours alone. Mark it for disposal (removed) and it sits on the desk until the bin round.
saying these in an interview costs you the question
- Saying detached means deleted, or confusing detached with removed.
- Believing you must call save/update on a managed entity for changes to be written.
- Assuming merge() mutates and manages the instance you passed in.
- Thinking remove() issues the DELETE immediately at the call site.
- Assuming any object that has a non-null id is managed.