Currently, it is common to use Hibernate for object persistence in databases, which is why it is quite important to know the Lifecycle of Entities in Hibernate and avoid possible errors.
What is Hibernate?
Hibernate is an ORM, which is an object-relational mapping tool between the database and Java. It allows mapping domain objects to objects in a relational database.
Hibernate Lifecycle states and Persistence Context
Each entity in Hibernate has its lifecycle:
- Transient
- Managed
- Detached
- Deleted
In addition to the Hibernate states, a concept that we must understand and take into account is the Persistence Context.
The Persistence Context is responsible for tracking all loaded data and synchronizing any changes with the database at the end of the transaction.
The use of the Persistence Context is implemented by JPA EntityManager and by Hibernate Session, so the Persistence Context is related to the different states of the Hibernate lifecycle.
Transient State in Hibernate Lifecycle
A transient state occurs in the Hibernate lifecycle when the Hibernate Session does not manage the object, and it is not yet persisted.
To persist a transient state object, we can use merge() or persist(). Take in mind that Hibernate versions equal to or less than 5 allow the use of save or saveOrUpdate, but version 6 deprecates this functionality.
Managed State in Hibernate Lifecycle
An entity in the managed state is a representation of a row in the database. Although it may not physically be in the database yet because it has not been persisted. An entity in the managed state is within the Hibernate Session, and any changes made in this state are propagated to the database.
Let’s see an example to better understand:
@Getter @Setter @Entity public class Department { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column private String name; }
Next, we will load the department named Human Resource into the Session. We rely on the Hibernate Session to do this:
Session session = sessionFactory.openSession(); Query query = session.createQuery("Select d from Department d where name= :name", Department.class); query.setParameter("name", "Human Resource"); department = (Department) query.getSingleResult(); Transaction transaction = session.getTransaction(); transaction.begin(); department.setName("Stuff"); transaction.commit();
When we use commit, Hibernate synchronizes the state with the database. An entity in the managed state always represents an entity that can be persisted. Therefore, to save changes to the database, we only need to use commit or flush. We don’t have to call any more methods or change the entity’s state.
Detached State Entity in Hibernate Lifecycle
An entity in the detached state represents our application object with a database row, differing from the managed state in that it has no tracking with the persistence context. When the session is closed or clear or evict is called, it detaches the state.
This is a typical Hibernate error that occurs when we have our entity in a detached state: org.hibernate.PersistentObjectException to JPA PersistenceException: detached entity passed to persist
Let’s start with our Department entity to get an entity in a detached state, and for that, we will use evict().
@Test public void given_department_when_detach_a_single_to_change_name_then_not_found() { Session session = sessionFactory.openSession(); Query query = session.createQuery("Select d from Department d where name= :name", Department.class); query.setParameter("name", "Human Resource"); department = (Department) query.getSingleResult(); session.evict(singleDepartment); department.setName("Accounts"); session.getTransaction().commit(); //No hay persistencia porque se encuentra en estado detach Query query = session.createQuery("Select d from Department d where name= :name", Department.class); query.setParameter("name", "Accounts"); department = (Department) query.getSingleResult(); Exception exception = assertThrows(NoResultException.class, () -> { query.getSingleResult() }); String expectedMessage = "No result found for query [Select d from Department d where name= :name]"; String actualMessage = exception.getMessage(); assertTrue(actualMessage.contains(expectedMessage)); }
To ensure to save the entity is neccesary to reintroduce the entity back into the Session using merge().
@Test public void given_department_when_detach_a_single_to_change_name_then_update() { Session session = sessionFactory.openSession(); Query query = session.createQuery("Select d from Department d where name= :name", Department.class); query.setParameter("name", "Human Resource"); department = (Department) query.getSingleResult(); session.evict(singleDepartment); department.setName("Accounts"); session.merge(department); session.getTransaction().commit(); Query query = session.createQuery("Select d from Department d where name= :name", Department.class); query.setParameter("name", "Accounts"); department = (Department) query.getSingleResult(); assertTrue("Accounts".equalsIgnoreCase(department.getName())); }
Delete state in Hibernate
When you invoke the remove(entity) method, it puts an entity in delete state. When you make a commit the Session will delete the entity.
@Test public void given_deparment_when_remove_from_session_then_department_is_removed() { session.beginTransaction(); Query query = session.createQuery("Select e from Department e where id= :id", Department.class); query.setParameter("id", department.getId()); department = (Department) query.getSingleResult(); session.remove(department); session.getTransaction().commit(); List<Department> departments = session.createQuery("Select d from Department d", Department.class).list(); assertEquals(departments.size(), 0); }
In the previous test, we deleted both from the Hibernate Session and from the database.
Conclusión
In this post, we have seen Lifecycle of Entities in Hibernate, as well as examples of each state. Understanding well the states that our Hibernate entities can go through will help us to solve errors and optimize our application.
If you need more information, you can leave us a comment or send an email to refactorizando.web@gmail.com You can also contact us through our social media channels on Facebook or twitter and we will be happy to assist you!!