Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

EntityManager и контекст персистентности

Вся работа с сущностями идёт через два объекта. EntityManagerFactory создаётся один раз на приложение: в нём настройки, сведения о классах и соединения с базой. EntityManager создаётся на одну единицу работы — например, на один веб-запрос — и после неё закрывается. Внутри каждого EntityManager живёт контекст персистентности (persistence context): набор объектов, которыми он сейчас управляет.

Транзакция вручную

Метод runInTransaction из прошлого урока — сокращение. Вот что он делает внутри:

// 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("Привет, Hibernate", "Моя первая статья")));
 
            EntityManager em = emf.createEntityManager();
            try {
                em.getTransaction().begin();
 
                Post a = em.find(Post.class, 1L);
                Post b = em.find(Post.class, 1L);
                IO.println("Один и тот же объект: " + (a == b));
 
                a.setTitle("Привет снова");
                IO.println("Заголовок изменён, фиксируем");
 
                em.getTransaction().commit();
            } finally {
                em.close();
            }
        }
    }
}

Для этого примера добавьте в Post сеттер setTitle. Вывод:

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=?
Один и тот же объект: true
Заголовок изменён, фиксируем
Hibernate: update Post set body=?,title=? where id=?

Что произошло

  1. Один запрос на два вызова. Первый find загрузил строку и положил объект в контекст персистентности. Второй find нашёл его там и в базу не ходил. Поэтому контекст персистентности ещё называют кешем первого уровня.
  2. Тот же самый объект. В пределах одного EntityManager одной строке всегда соответствует ровно один Java-объект, поэтому a == b.
  3. Обновление без вызова сохранения. При фиксации Hibernate сравнивает управляемые объекты с тем состоянием, которое загрузил, и записывает разницу. Это называется dirty checking — проверка изменений. Метода save() или update() нет: достаточно поменять поле управляемого объекта.

Session

У Hibernate есть собственные интерфейсы с теми же ролями: SessionFactory и Session. Они расширяют EntityManagerFactory и EntityManager и добавляют методы, которых нет в стандарте. Объект Hibernate всегда можно получить из стандартного:

// 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("В базе: " + post.getTitle());
});

Напечатается В базе: Привет снова — изменение действительно сохранилось. Session импортируется из org.hibernate.

В Spring Boot

В Spring-приложении EntityManager самостоятельно не создают. Spring создаёт его на каждый запрос, а метод с аннотацией @Transactional получает транзакцию. Чаще всего к EntityManager даже не обращаются напрямую: его вызывают репозитории Spring Data.

Частая ошибка

EntityManager без транзакции ничего не сохраняет. persist молча запоминает объект, а при закрытии менеджера он просто забывается:

// src/main/java/com/example/blog/Main.java
EntityManager em = emf.createEntityManager();
em.persist(new Post("Потерянная статья", "Без транзакции"));
em.close();

Подсчёт после этого покажет Статей: 0. Если попытаться принудительно записать изменения через em.flush(), будет ошибка:

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

Любые изменения данных должны происходить между begin() и commit() — или внутри runInTransaction.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.