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().