Правильное использование флеша() в 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 может получить сгенерированный идентификатор и правильно управлять отношениями)