← Назад к вопросам
Когда происходит запись нескольких сущностей в Persistence Context?
3.0 Senior🔥 131 комментариев
#ORM и Hibernate
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Когда происходит запись в Persistence Context
Persistence Context — это кэш сущностей в памяти приложения. Понимание моментов записи критично для работы с Hibernate и JPA.
Основной момент: flush()
Сущности записываются в БД при вызове flush(). Это не commit, а отправка SQL команд.
public class PersistenceContextExample {
@Autowired
private EntityManager em;
public void demonstrateFlush() {
// 1. Создаем сущность (в памяти, SQL еще нет)
Author author = new Author("John Doe");
em.persist(author); // Добавлена в Persistence Context
// SQL INSERT еще не выполнен!
// 2. Изменяем сущность
author.setName("Jane Doe");
// 3. Flush() — происходит запись в БД
em.flush(); // Теперь выполнены INSERT и UPDATE
// 4. Commit() — фиксирует транзакцию
// em.getTransaction().commit();
}
}
Когда автоматически вызывается flush()?
1. При commit() транзакции (САМЫЙ ЧАСТЫЙ СЛУЧАЙ)
@Transactional // Аннотация Spring
public void createAuthorsAndBooks() {
Author author1 = new Author("Isaac Asimov");
Author author2 = new Author("Arthur Clarke");
em.persist(author1);
em.persist(author2);
Book book = new Book("Foundation", author1);
em.persist(book);
// Здесь автоматически вызывается flush() и затем commit()
// Все 3 INSERT выполняются в БД
}
// При выходе из @Transactional метода:
// 1. Вызывается flush()
// 2. Затем commit()
Это 80% случаев — flush происходит при завершении транзакции.
2. При выполнении SQL запроса внутри транзакции
@Transactional
public void flushBeforeQuery() {
Author author = new Author("Philip K. Dick");
em.persist(author); // В Persistence Context
// Выполняем JPQL запрос
List<Author> authors = em.createQuery(
"SELECT a FROM Author a WHERE a.name = 'Philip K. Dick'",
Author.class
).getResultList();
// Hibernate автоматически вызвал flush() ПЕРЕД запросом!
// Иначе мы не получили бы только что сохраненного автора
// Результат: authors содержит нашего только что созданного автора
}
Это важно помнить: перед любым SELECT автоматически вызывается flush().
3. При явном вызове flush()
@Transactional
public void explicitFlush() {
Author author = new Author("Frank Herbert");
em.persist(author);
em.flush(); // Явно отправляем в БД
System.out.println(author.getId()); // ID уже установлен!
// Но если тут произойдет исключение,
// транзакция откатится и INSERT не будет сохранен
}
4. При работе с нативными SQL запросами
@Transactional
public void nativeQuery() {
Author author = new Author("N.K. Jemisin");
em.persist(author);
// Нативный SQL запрос
em.createNativeQuery(
"SELECT * FROM authors WHERE name = ?"
).setParameter(1, "N.K. Jemisin").getResultList();
// Hibernate вызовет flush() перед выполнением нативного запроса
// Это может быть медленным для больших Persistence Context
}
Несколько сущностей одновременно
@Transactional
public void saveMultipleEntities() {
// Добавляем несколько сущностей
for (int i = 0; i < 1000; i++) {
Author author = new Author("Author " + i);
em.persist(author);
}
// ВСЕ 1000 сущностей находятся в Persistence Context
// Это занимает память!
// При commit() все 1000 INSERT выполнятся ОДНОЙ ночью (или батчами)
// благодаря параметру: spring.jpa.properties.hibernate.jdbc.batch_size
}
Управление flush mode
@Transactional
public void flushModes() {
Author author = new Author("Ray Bradbury");
em.persist(author);
// AUTO (по умолчанию) — flush перед SELECT
// COMMIT — flush только при commit
// MANUAL — только при явном вызове flush()
em.setFlushMode(FlushModeType.COMMIT);
List<Authors> authors = em.createQuery(
"SELECT a FROM Author a",
Author.class
).getResultList();
// Ray Bradbury может быть не в результатах, потому что
// flush еще не произошел (FlushModeType.COMMIT)
}
Практические следствия
Проблема: Persistence Context слишком большой
@Transactional
public void processMillion() {
for (int i = 0; i < 1_000_000; i++) {
Author author = new Author("Author " + i);
em.persist(author);
if (i % 1000 == 0) {
em.flush(); // Отправляем батч в БД
em.clear(); // Очищаем кэш (ВАЖНО!)
}
}
}
Без em.clear() все 1 млн сущностей останутся в памяти, потребляя ГБ ОЗУ!
Проблема: Lost updates
@Transactional
public void concurrentUpdate() {
Author author = em.find(Author.class, 1L); // Версия 1
author.setName("New Name");
// Другой поток обновил того же автора
// Когда произойдет flush():
// - Наша версия 1, в БД уже версия 2
// - Будет OptimisticLockException
}
Ключевые точки
- Flush != Commit — flush отправляет SQL, commit фиксирует транзакцию
- Автоматический flush при SELECT, commit, явном вызове
- Batch size оптимизирует отправку нескольких INSERT в одном запросе
- Em.clear() освобождает память при обработке больших объемов
Вывод: Запись нескольких сущностей происходит при flush(), который вызывается автоматически при commit() или SELECT, или явно через em.flush(). Это один из самых важных концептов в Hibernate.