Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

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

StateMeaningHow it gets there
Transient (new)Just created with new, Hibernate does not know about it, usually no idnew Post(...)
ManagedIn the persistence context: changes are saved automaticallypersist, find, a query, merge result
DetachedHas an id and a row in the database, but its EntityManager is closedthe EntityManager is closed, detach, clear
RemovedScheduled for deletion; the row disappears at commitremove

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

  1. A new object has no id: Hibernate has not seen it yet.
  2. After persist the object is managed and already has an id.
  3. When the transaction ended, the EntityManager closed and the object became detached. Changing its title affected only the Java object — no update was sent.
  4. merge does not attach the passed object. It loads a managed copy, copies the fields into it and returns the copy. The original stays detached.
  5. After remove the object is no longer in the context, and the delete runs 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.Post

To save changes to an object that already exists in the database, use merge.

Thanks for reading!

Donate to the site — we work hard for you.