Careeroza
The Four States
States and Transitions
new Product(...)
|
v
+-----------+ persist() +-----------+ remove() +-----------+
| TRANSIENT | -------------> | MANAGED | ------------> | REMOVED |
+-----------+ +-----------+ <------------ +-----------+
^ | ^ persist() |
find()/query | | | merge() | commit -> DELETE
| | | v
(database) --+ | +------+ (row gone)
| |
detach()/clear() | +-----------+
close()/commit* +--> | DETACHED |
+-----------+
* in Spring, when the transaction (and its EntityManager) endsWhat Each State Means
| State | Has id / row? | In persistence context? | Changes saved? |
|---|---|---|---|
| Transient (new) | No row yet (id usually null) | No | No — Hibernate does not know it exists |
| Managed (persistent) | Yes | Yes | Yes, automatically at flush |
| Detached | Yes | No (context closed or entity detached) | No — needs merge |
| Removed | Yes, until commit | Yes, scheduled for deletion | DELETE at flush |
Lifecycle, Simulated
Example
A small state machine that follows one product through every state, applying the same rules Hibernate enforces:
import java.util.*;
public class Main {
public static void main(String[] args) {
Context em = new Context();
Product p = new Product("Boat Rockerz 450");
em.print(p); // transient
em.persist(p); em.print(p); // managed
em.detach(p); em.print(p); // detached
try { em.remove(p); } catch (IllegalArgumentException e) { System.out.println("remove failed: " + e.getMessage()); }
Product managed = em.merge(p); em.print(managed);
System.out.println("merge returned the same object? " + (managed == p));
em.remove(managed); em.print(managed);
}
}
enum State { TRANSIENT, MANAGED, DETACHED, REMOVED }
class Product {
Long id; final String name;
Product(String name) { this.name = name; }
}
class Context {
private final Map<Long, Product> managed = new LinkedHashMap<>();
private final Set<Long> removed = new TreeSet<>();
private final Set<Long> knownIds = new TreeSet<>(); // rows that exist in the "database"
private long nextId = 1;
State stateOf(Product p) {
if (p.id == null) return State.TRANSIENT;
if (managed.get(p.id) == p) return removed.contains(p.id) ? State.REMOVED : State.MANAGED;
return State.DETACHED;
}
void print(Product p) { System.out.println(p.name + " (id=" + p.id + ") -> " + stateOf(p)); }
void persist(Product p) { p.id = nextId++; knownIds.add(p.id); managed.put(p.id, p); }
void detach(Product p) { managed.remove(p.id); }
void remove(Product p) {
if (stateOf(p) != State.MANAGED) throw new IllegalArgumentException("Removing a detached instance Product#" + p.id);
removed.add(p.id);
}
Product merge(Product p) {
Product copy = new Product(p.name); // Hibernate loads the row, then copies state onto it
copy.id = p.id;
managed.put(copy.id, copy);
return copy;
}
}Output
Boat Rockerz 450 (id=null) -> TRANSIENT Boat Rockerz 450 (id=1) -> MANAGED Boat Rockerz 450 (id=1) -> DETACHED remove failed: Removing a detached instance Product#1 Boat Rockerz 450 (id=1) -> MANAGED merge returned the same object? false Boat Rockerz 450 (id=1) -> REMOVED