Что делает метод refresh()
Метод refresh() перезагружает сущность из базы данных, заменяя текущие значения в памяти актуальными данными из БД. Это полезно при работе с триггерами, конкурентными обновления...
Обзор
Метод refresh() перезагружает сущность из базы данных, заменяя текущие значения в памяти актуальными данными из БД. Это полезно при работе с триггерами, конкурентными обновлениями и когда нужно гарантировать актуальность данных.
🟢 Junior Level
Что делает refresh()
refresh() — перезагружает сущность из базы данных, заменяя текущие значения в памяти.
User user = entityManager.find(User.class, 1L);
user.setName("Changed in memory"); // изменили в памяти
// Другая транзакция изменила БД
// Или триггер изменил данные
entityManager.refresh(user); // перезагрузить из БД
// Теперь user имеет актуальные значения из БД
// "Changed in memory" — перезаписано
Когда использовать
- После триггеров — база изменила поля через триггер
- При конкурентных обновлениях — другая транзакция обновила данные
- Для получения актуальных данных — когда есть сомнения в актуальности
refresh vs detach
refresh() — обновить данные из БД (сущность остаётся managed)
detach() — отсоединить сущность (без обновления из БД)
Пример
@Transactional
public void updateUser(User user) {
entityManager.persist(user);
// Триггер в БД установил created_at
entityManager.refresh(user);
// Теперь user.getCreatedAt() содержит значение из БД
}
🟡 Middle Level
Детальное использование
// После триггера
User user = new User();
user.setName("John");
entityManager.persist(user);
// flush → триггер в БД установил created_at
entityManager.flush();
entityManager.refresh(user); // получить created_at из БД
// При конкурентных обновлениях
User user = entityManager.find(User.class, 1L);
// Другая транзакция обновила user
entityManager.refresh(user); // синхронизировать с БД
refresh с LockMode
// OPTIMISTIC — проверка version
entityManager.refresh(user, LockModeType.OPTIMISTIC);
// PESSIMISTIC_READ — блокировка на чтение
entityManager.refresh(user, LockModeType.PESSIMISTIC_READ);
// PESSIMISTIC_WRITE — блокировка на запись
entityManager.refresh(user, LockModeType.PESSIMISTIC_WRITE);
// С таймаутом
Map<String, Object> properties = Map.of(
"jakarta.persistence.lock.timeout", 5000
);
entityManager.refresh(user, LockModeType.PESSIMISTIC_WRITE, properties);
Типичные ошибки
// ❌ refresh без причины
User user = entityManager.find(User.class, 1L);
entityManager.refresh(user); // ❌ лишний SELECT!
// ❌ refresh для detached
User detached = getDetachedUser();
entityManager.refresh(detached); // ❌ IllegalArgumentException
// ✅ refresh только для managed
User managed = entityManager.find(User.class, 1L);
entityManager.refresh(managed); // ✅
Когда refresh НЕ нужен
// ❌ Не нужен refresh после persist, если в БД нет триггеров/генерируемых колонок. Если триггер устанавливает поля — refresh нужен (см. Junior пример).
User user = new User();
entityManager.persist(user);
entityManager.refresh(user); // ❌ данные те же самые
// ❌ Не нужен refresh после merge
User managed = entityManager.merge(detached);
entityManager.refresh(managed); // ❌ данные уже актуальны
// ✅ Нужен после триггеров
entityManager.persist(user);
entityManager.flush();
entityManager.refresh(user); // ✅ триггер мог изменить данные
🔴 Senior Level
Внутренняя реализация
refresh():
1. Проверить что entity managed (не detached)
2. Выполнить SELECT * FROM table WHERE id = ?
3. Обновить все поля entity из результата
4. Обновить snapshot в persistence context
5. Обновить EntityEntry
Если указан LockMode:
- Добавить LOCK clause к SELECT
- Проверить version для OPTIMISTIC
refresh и генерация ID
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(insertable = false, updatable = false)
private LocalDateTime createdAt; // установлено триггером
}
User user = new User();
entityManager.persist(user);
entityManager.flush();
entityManager.refresh(user); // получить createdAt из БД
refresh и lazy loading
User user = entityManager.find(User.class, 1L);
user.getOrders().size(); // загрузить коллекцию
// Другая транзакция изменила коллекцию
entityManager.refresh(user); // перезагрузить ВСЁ включая коллекции
// Или только коллекцию
entityManager.refresh(user, Map.of(
"org.hibernate.refreshMode", RefreshMode.FETCH
));
// (Доступно в Hibernate 6+)
Продвинутые паттерны
// Pattern: refresh после bulk update
@Modifying
@Query("UPDATE User u SET u.status = :status WHERE u.age > :age")
int bulkUpdate(@Param("status") String status, @Param("age") int age);
// После bulk update — refresh affected entities
User user = entityManager.find(User.class, userId);
entityManager.refresh(user); // получить актуальные данные
Best Practices
✅ refresh() после триггеров БД
✅ refresh() при конкурентных изменениях
✅ С LockMode когда нужна блокировка
✅ flush() перед refresh() для синхронизации
❌ refresh() без причины (лишний SELECT)
❌ refresh() для detached entities
❌ refresh() вместо dirty checking
❌ refresh() после persist/merge без необходимости
🎯 Шпаргалка для интервью
Обязательно знать:
- refresh() перезагружает сущность из БД, заменяя текущие значения
- Используется после триггеров, при конкурентных обновлениях, для актуальности данных
- Поддерживает LockMode: OPTIMISTIC, PESSIMISTIC_READ, PESSIMISTIC_WRITE
- Работает только для managed сущностей, для detached — IllegalArgumentException
- В Hibernate 6+ есть RefreshMode.FETCH для загрузки только коллекций
Частые уточняющие вопросы:
- Когда refresh() нужен? После триггеров БД, при конкурентных изменениях, после bulk update
- refresh vs detach? refresh — обновляет данные из БД (остаётся managed), detach — отсоединяет (без обновления)
- Почему refresh после persist бесполезен? Данные ещё не изменились, лишний SELECT
- Можно ли refresh с таймаутом? Да, через properties: jakarta.persistence.lock.timeout
Красные флаги (НЕ говорить):
- «refresh() после каждого persist» — лишний SELECT, данные те же
- «refresh() для detached» — IllegalArgumentException
- «refresh() вместо dirty checking» — dirty checking автоматический, refresh принудительный
- «Не понимаю когда refresh нужен» — только триггеры, конкурентные изменения, bulk update
Связанные темы:
- [[8. Что такое состояния transient, persistent, detached, removed]]
- [[14. В чём разница между persist() и merge()]]
- [[17. Как реализовать оптимистичную блокировку в JPA]]
- [[18. Как реализовать пессимистичную блокировку в JPA]]