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

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

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

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

Ответ 1

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

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

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

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

Ответ 2

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

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

Ответ 3

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

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

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

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