Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

EntityManager and the persistence context

All work with entities goes through two objects. EntityManagerFactory is created once per application: it holds the settings, the mapping of classes and the database connections. EntityManager is created for one unit of work — for example, one web request — and closed afterwards. Inside each EntityManager lives a persistence context: the set of objects it currently manages.

A manual transaction

The runInTransaction method from the previous lesson is a shortcut. Here is what it does inside:

// src/main/java/com/example/blog/Main.java
package com.example.blog;
 
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.Persistence;
 
public class Main {
    public static void main(String[] args) {
        try (EntityManagerFactory emf = Persistence.createEntityManagerFactory("blog")) {
            emf.runInTransaction(em -> em.persist(new Post("Hello, Hibernate", "My first post")));
 
            EntityManager em = emf.createEntityManager();
            try {
                em.getTransaction().begin();
 
                Post a = em.find(Post.class, 1L);
                Post b = em.find(Post.class, 1L);
                IO.println("Same object: " + (a == b));
 
                a.setTitle("Hello again");
                IO.println("Title changed, committing");
 
                em.getTransaction().commit();
            } finally {
                em.close();
            }
        }
    }
}

Add a setTitle setter to Post for this example. Output:

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=?
Same object: true
Title changed, committing
Hibernate: update Post set body=?,title=? where id=?

What happened

  1. One query for two calls. The first find loaded the row and put the object into the persistence context. The second find found it there and did not go to the database. That is why the persistence context is also called the first-level cache.
  2. The same object. Within one EntityManager one row always corresponds to exactly one Java object, so a == b.
  3. An update without a save call. At commit Hibernate compares the managed objects with the state it loaded and writes the differences. This is called dirty checking. There is no save() or update() method: changing a field of a managed object is enough.

Session

Hibernate has its own interfaces with the same roles: SessionFactory and Session. They extend EntityManagerFactory and EntityManager and add Hibernate-specific methods. You can always get the Hibernate object from the standard one:

// src/main/java/com/example/blog/Main.java
emf.runInTransaction(em -> {
    Session session = em.unwrap(Session.class);
    Post post = session.find(Post.class, 1L);
    IO.println("In the database: " + post.getTitle());
});

It prints In the database: Hello again — the change really was saved. Session is imported from org.hibernate.

In Spring Boot

In a Spring application you never create an EntityManager yourself. Spring creates one per request, and a method marked @Transactional gets a transaction. More often you don't touch the EntityManager at all: Spring Data repositories call it for you.

Common mistake

An EntityManager without a transaction does not save anything. persist silently remembers the object, and when the manager is closed, it is simply forgotten:

// src/main/java/com/example/blog/Main.java
EntityManager em = emf.createEntityManager();
em.persist(new Post("Lost post", "No transaction"));
em.close();

A count afterwards prints Posts: 0. If you try to force the write with em.flush(), you get an error:

Exception in thread "main" jakarta.persistence.TransactionRequiredException: No active transaction

Every change to data must happen between begin() and commit() — or inside runInTransaction.

Thanks for reading!

Donate to the site — we work hard for you.