Gymterview
middle

Как работает @Transactional в контексте JPA?

@Transactional в контексте JPA управляет не только транзакциями БД, но и жизненным циклом Persistence Context – «рабочей области» JPA, которая хранит загруженные сущности и отслеживает их изменения (кэш первого уровня).

Dirty Checking

Пример
@Transactional
public void updateUserEmail(Long userId, String newEmail) {
    User user = userRepository.findById(userId).orElseThrow();
    // user в состоянии MANAGED
    user.setEmail(newEmail);
    // НЕ нужно вызывать save()!
    // Hibernate обнаружит изменение (dirty checking) и выполнит UPDATE при коммите
}

Состояния сущности JPA

Состояние Описание
NEW (Transient) Новый объект, не связан с контекстом и БД
MANAGED Управляется контекстом, изменения отслеживаются
DETACHED Был управляемым, но контекст закрыт
REMOVED Помечен для удаления

readOnly и оптимизация

Пример
@Transactional(readOnly = true)
public User getUser(Long id) {
    // readOnly = true:
    // 1. Не делать dirty checking (экономия CPU)
    // 2. Не хранить snapshot-ы (экономия памяти)
    // 3. Потенциально использовать read-реплику
    return userRepository.findById(id).orElseThrow();
}

Open Session in View (OSIV)

По умолчанию в Spring Boot включён spring.jpa.open-in-view=true, что держит Persistence Context открытым на протяжении всего HTTP-запроса. Рекомендуется отключить:

Пример
spring.jpa.open-in-view=false

На собеседовании: ключевое – dirty checking (автоматическое обнаружение изменений) и состояния сущностей. Частая ошибка – изменять сущность внутри @Transactional метода, не осознавая, что Hibernate выполнит UPDATE даже без вызова save().