Entity states
An entity object is always in one of four states. The state decides whether Hibernate tracks changes to the object and what each EntityManager method does with it. Most confusing situations — "I changed the field, but nothing was saved" — come down to the object being in a different state than you think.
The four states
| State | Meaning | How it gets there |
|---|---|---|
| Transient (new) | Just created with new, Hibernate does not know about it, usually no id | new Post(...) |
| Managed | In the persistence context: changes are saved automatically | persist, find, a query, merge result |
| Detached | Has an id and a row in the database, but its EntityManager is closed | the EntityManager is closed, detach, clear |
| Removed | Scheduled for deletion; the row disappears at commit | remove |
Following one object
// src/main/java/com/example/blog/Main.java
package com.example.blog;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.Persistence;
public class Main {
public static void main(String[] args) {
try (EntityManagerFactory emf = Persistence.createEntityManagerFactory("blog")) {
Post post = new Post("States", "Transient, managed, detached, removed");
IO.println("1. transient: id = " + post.getId());
emf.runInTransaction(em -> {
em.persist(post);
IO.println("2. managed: contains = " + em.contains(post) + ", id = " + post.getId());
});
post.setTitle("Changed while detached");
String title = emf.callInTransaction(em -> em.find(Post.class, post.getId()).getTitle());
IO.println("3. detached: database still has '" + title + "'");
emf.runInTransaction(em -> {
Post managed = em.merge(post);
IO.println("4. merge returned the same object: " + (managed == post));
em.remove(managed);
IO.println("5. removed: contains = " + em.contains(managed));
});
}
}
}em.contains(object) answers whether the object is in this persistence context. Output:
1. transient: id = null
Hibernate: select next value for Post_SEQ
2. managed: contains = true, id = 1
Hibernate: insert into Post (body,title,id) values (?,?,?)
Hibernate: select p1_0.id,p1_0.body,p1_0.title from Post p1_0 where p1_0.id=?
3. detached: database still has 'States'
Hibernate: select p1_0.id,p1_0.body,p1_0.title from Post p1_0 where p1_0.id=?
4. merge returned the same object: false
5. removed: contains = false
Hibernate: delete from Post where id=?What the output shows
- A new object has no id: Hibernate has not seen it yet.
- After
persistthe object is managed and already has an id. - When the transaction ended, the
EntityManagerclosed and the object became detached. Changing its title affected only the Java object — noupdatewas sent. mergedoes not attach the passed object. It loads a managed copy, copies the fields into it and returns the copy. The original stays detached.- After
removethe object is no longer in the context, and thedeleteruns at commit.
In a web application
Detached objects are normal in web applications: an entity loaded in one request lives in a form and returns in another request. Either load the entity again with find and copy the new values into it, or pass the detached object to merge.
Common mistake
persist is only for new objects. If you pass it a detached object — it already has an id — Hibernate refuses:
// src/main/java/com/example/blog/Main.java
post.setTitle("Edited");
emf.runInTransaction(em -> em.persist(post));Exception in thread "main" jakarta.persistence.EntityExistsException: Detached entity passed to persist: com.example.blog.PostTo save changes to an object that already exists in the database, use merge.