Правильное использование flush () в JPA / Hibernate

111

Я собирал информацию о методе flush (), но не совсем понимаю, когда его использовать и как правильно использовать. Из того, что я прочитал, я понимаю, что содержимое контекста постоянства будет синхронизировано с базой данных, то есть выдача невыполненных инструкций или обновление данных объекта.

Теперь у меня есть следующий сценарий с двумя объектами Aи B(в отношении один-к-одному, но не принудительно или смоделированным JPA). Aимеет составной PK, который устанавливается вручную, а также имеет автоматически сгенерированное поле IDENTITY recordId. Это recordIdдолжно быть записано в объект Bкак внешний ключ для A. Сохраняю Aи Bв единой транзакции. Проблема заключается в том, что автоматически сгенерированное значение A.recordIdне доступно в сделке, если не сделать явный вызов em.flush()после вызова em.persist()на A. (Если у меня есть автоматически сгенерированный PK IDENTITY, значение обновляется непосредственно в сущности, но здесь это не так.)

Может ли em.flush()причинить вред при использовании в транзакции?

MicSim
источник

Ответы:

150

Вероятно, точные детали em.flush()зависят от реализации. Как правило, в любом случае провайдеры JPA, такие как Hibernate, могут кэшировать инструкции SQL, которые они должны отправлять в базу данных, часто до тех пор, пока вы фактически не зафиксируете транзакцию. Например, при вызове em.persist()Hibernate запоминает, что он должен выполнить INSERT базы данных, но фактически не выполняет инструкцию, пока вы не зафиксируете транзакцию. Афайк, в основном это сделано из соображений производительности.

В некоторых случаях в любом случае вы хотите, чтобы инструкции SQL выполнялись немедленно; обычно, когда вам нужен результат некоторых побочных эффектов, таких как автоматически сгенерированный ключ или триггер базы данных.

Что em.flush()нужно, так это очистить внутренний кеш инструкций SQL и немедленно выполнить его в базе данных.

Итог: никакого вреда не будет, только у вас может быть (незначительное) снижение производительности, так как вы переопределяете решения поставщика JPA в отношении наилучшего времени для отправки инструкций SQL в базу данных.

Флавио
источник
1
Что он сказал. Поведение em.flush () перекликается с java.io.Flushable.flush (), где все буферизованные данные отправляются в любое подходящее место назначения.
Эрик
4
если flush () отправляет данные в базу данных? что произойдет, если после этого будет сгенерировано исключение? Будет ли диспетчер сущностей все откатить? даже данные, записанные при первом флеше?
Jaime Hablutzel
101
flush () отправляет SQL-инструкции в базу данных, такие как INSERT, UPDATE и т. д. Он не отправляет COMMIT, поэтому, если у вас есть исключение после flush (), вы все равно можете выполнить полный откат.
Flavio
17
Вы можете откатить БД, но это не приведет к откату каких-либо изменений в объектах, например, автоматически увеличивающейся «версии», автоматически сгенерированного идентификатора и т. Д. Кроме того, диспетчер сущностей будет закрыт после отката. Просто помните, что если вы попытаетесь «объединить» объект с другим сеансом, автоматически увеличивающаяся «версия» может вызвать исключение OptimisticLockException.
Питер Дэвис
11
Помимо запуска побочных эффектов, еще одна причина для использования flush () - это если вы хотите иметь возможность считывать эффекты операции в базе данных с использованием JPQL / HQL (например, в тесте). JPA не может использовать кэшированные данные при выполнении этих запросов, поэтому будет прочитано только то, что действительно находится в БД.
sleske
2

На самом деле, em.flush()делать больше, чем просто отправлять кэшированные команды SQL. Он пытается синхронизировать контекст постоянства с базовой базой данных. Это может привести к значительным затратам времени на ваши процессы, если ваш кеш содержит коллекции, которые необходимо синхронизировать.

Осторожно при его использовании.

Рикардо Накашима
источник
2

Может ли em.flush () причинить вред при использовании в транзакции?

Да, он может удерживать блокировки в базе данных дольше, чем необходимо.

Как правило, при использовании JPA вы делегируете управление транзакциями контейнеру (также известному как CMT - с использованием аннотации @Transactional для бизнес-методов), что означает, что транзакция автоматически запускается при входе в метод и фиксируется / откатывается в конце. Если вы позволите EntityManager обрабатывать синхронизацию базы данных, выполнение операторов sql будет запускаться только непосредственно перед фиксацией, что приведет к кратковременным блокировкам в базе данных. В противном случае ваши вручную очищенные операции записи могут сохранять блокировки между ручным сбросом и автоматической фиксацией, которые могут быть долгими в зависимости от оставшегося времени выполнения метода.

Отмечает, что некоторые операции автоматически запускают сброс: выполнение собственного запроса к тому же сеансу (состояние EM должно быть сброшено, чтобы оно стало доступным для запроса SQL), вставка сущностей с использованием собственного сгенерированного идентификатора (сгенерированного базой данных, поэтому оператор вставки должен быть запускается, таким образом, EM может получить сгенерированный идентификатор и правильно управлять отношениями)

Габ
источник