EntityManager.merge()
может вставлять новые объекты и обновлять существующие.
Почему один хочет использовать persist()
(который может создавать только новые объекты)?
jpa
merge
entitymanager
persist
java-persistence-api
Аарон Дигулла
источник
источник
Ответы:
В любом случае вы добавите объект в PersistenceContext, разница в том, что вы будете делать с объектом позже.
Persist берет экземпляр объекта, добавляет его в контекст и управляет этим экземпляром (т.е. будущие обновления объекта будут отслеживаться).
Слияние возвращает управляемый экземпляр, в который было объединено состояние. Он возвращает то, что существует в PersistenceContext, или создает новый экземпляр вашей сущности. В любом случае он скопирует состояние из предоставленного объекта и вернет управляемую копию. Экземпляр, который вы передаете, не будет управляемым (любые внесенные вами изменения не будут частью транзакции - если вы не вызовете merge снова). Вы можете через использование возвращенного экземпляра (управляемого).
Может быть, пример кода поможет.
Сценарии 1 и 3 примерно эквивалентны, но в некоторых ситуациях вы хотите использовать сценарий 2.
источник
merge
, полная копия объекта, прежде чем управлять им, снижает производительность?@GeneratedId
я могу получить это в сценарии 2?Persist и merge предназначены для двух разных целей (они вовсе не являются альтернативами).
(отредактировано для расширения информации о различиях)
сохраняются:
слияния:
Persist () эффективность:
Семантика persist ():
Пример:
Таким образом, существует только 1 прикрепленный объект для любого регистра в менеджере сущностей.
merge () для сущности с идентификатором выглядит примерно так:
Хотя при подключении к MySQL функция merge () может быть столь же эффективной, как и persist () с использованием вызова INSERT с опцией ON DUPLICATE KEY UPDATE, JPA - это программирование очень высокого уровня, и вы не можете предполагать, что это будет иметь место везде.
источник
em.persist(x)
сx = em.merge(x)
?merge()
может также броситьEntityExistsException
RuntimeException
, но это не упоминается в Javadoc.Если вы используете назначенный генератор, использование слияния вместо постоянного может привести к созданию избыточного оператора SQL , что повлияет на производительность.
Кроме того, вызов слияния для управляемых объектов также является ошибкой, поскольку Hibernate автоматически управляет управляемыми объектами, и их состояние синхронизируется с записью базы данных с помощью механизма грязной проверки после сброса контекста постоянства .
Чтобы понять, как все это работает, вы должны сначала знать, что Hibernate переключает мышление разработчика с операторов SQL на переходы состояний сущностей .
Когда Hibernate активно управляет сущностью, все изменения будут автоматически распространяться в базу данных.
Hibernate отслеживает подключенные объекты. Но для того, чтобы субъект стал управляемым, он должен находиться в правильном состоянии.
Чтобы лучше понять переходы состояния JPA, вы можете представить следующую диаграмму:
Или, если вы используете специальный API Hibernate:
Как показано на приведенных выше диаграммах, объект может находиться в одном из следующих четырех состояний:
Новый (Переходный)
Вновь созданный объект, который никогда не был связан с Hibernate
Session
(akaPersistence Context
) и не сопоставлен ни с одной строкой таблицы базы данных, считается находящимся в состоянии New (Transient).Чтобы стать постоянными, нам нужно либо явно вызвать
EntityManager#persist
метод, либо использовать транзитивный механизм персистентности.Постоянный (управляемый)
Постоянный объект был связан со строкой таблицы базы данных, и он управляется текущим контекстом постоянства. Любые изменения, внесенные в такой объект, будут обнаружены и распространены в базе данных (во время сброса сеанса). С Hibernate нам больше не нужно выполнять операторы INSERT / UPDATE / DELETE. Hibernate использует транзакционный стиль записи с обратной записью, и изменения синхронизируются в самый последний ответственный момент, во время текущего времени
Session
сброса.отдельный
Как только текущий контекст сохраняемости закрывается, все ранее управляемые объекты становятся отсоединенными. Последовательные изменения больше не будут отслеживаться, и автоматическая синхронизация базы данных не произойдет.
Чтобы связать отдельную сущность с активным сеансом Hibernate, вы можете выбрать один из следующих вариантов:
Повторное прикрепление
Hibernate (но не JPA 2.1) поддерживает подключение через метод Session # update. Сеанс Hibernate может связать только один объект Entity для данной строки базы данных. Это связано с тем, что постоянный контекст действует как кэш в памяти (кэш первого уровня) и только одно значение (сущность) связано с данным ключом (тип сущности и идентификатор базы данных). Объект может быть присоединен повторно, только если нет другого объекта JVM (соответствующего той же строке базы данных), уже связанного с текущим сеансом Hibernate.
сращивание
Слияние собирается скопировать состояние отдельного объекта (источник) в экземпляр управляемого объекта (место назначения). Если объединяемый объект не имеет эквивалента в текущем сеансе, он будет выбран из базы данных. Экземпляр отсоединенного объекта будет оставаться отсоединенным даже после операции слияния.
Удалены
Хотя JPA требует, чтобы разрешалось удалять только управляемые объекты, Hibernate также может удалять отдельные объекты (но только с помощью вызова метода Session # delete). Удаленный объект запланирован только для удаления, и фактический оператор DELETE базы данных будет выполнен во время сброса сеанса.
источник
Я заметил, что когда я использовал
em.merge
, я получалSELECT
утверждение для каждогоINSERT
, даже когда не было никакого поля, которое JPA генерировал для меня - поле первичного ключа было UUID, который я установил сам. Я переключился наem.persist(myEntityObject)
и получил толькоINSERT
заявления тогда.источник
merge()
. У меня была база данных PostgreSQL со сложным представлением : представление агрегированных данных из нескольких таблиц (таблицы имели одинаковую структуру, но разные имена). Так что JPA попытался сделатьmerge()
, но на самом деле JPA сначала сделалSELECT
(база данных из-за настроек представления может вернуть несколько записей с одним и тем же первичным ключом из разных таблиц!), Затем JPA (Hibernate был реализацией) не удалось: есть несколько записей с одним и тем же ключом (org.hibernate.HibernateException: More than one row with the given identifier was found
). В моем случаеpersist()
мне помогли.В спецификации JPA говорится следующее
persist()
.Поэтому использование
persist()
будет целесообразным, когда объект не должен быть отдельным объектом. Вы можете предпочесть, чтобы код выдавал код,PersistenceException
чтобы он быстро завершался с ошибкой.Хотя спецификация неясна ,
persist()
может установить@GeneratedValue
@Id
для объекта.merge()
однако должен иметь объект с@Id
уже сгенерированным.источник
merge()
однако должен иметь объект с@Id
уже сгенерированным . " Всякий раз, когда EntityManager не находит значение для поля идентификатора объекта, оно сохраняется (вставляется) в БД.Еще некоторые подробности о слиянии, которые помогут вам использовать слияние по-прежнему:
Вся вышеупомянутая информация была взята из «Pro JPA 2 Mastering Java ™ Persistence API» Майка Кейта и Меррика Шникариола. Глава 6. Раздел отрыв и слияние. Эта книга на самом деле вторая книга, посвященная JPA авторами. Эта новая книга имеет много новой информации, чем прежняя. Я действительно рекомендовал прочитать эту книгу для тех, кто будет серьезно связан с JPA. Прошу прощения за анонимную публикацию моего первого ответа.
источник
Есть еще несколько различий между
merge
иpersist
(я снова перечислю те, что были размещены здесь):D1.
merge
не делает переданный объект управляемым, а возвращает другой управляемый экземпляр.persist
с другой стороны сделаем переданный объект управляемым:D2. Если вы удалите сущность, а затем решите сохранить ее обратно, вы можете сделать это только с помощью persist (), потому что
merge
будет выброшеноIllegalArgumentException
.D3. Если вы решили позаботиться о своих идентификаторах вручную (например, с помощью идентификаторов UUID), то
merge
операция будет инициировать последующиеSELECT
запросы для поиска существующих объектов с таким идентификатором, хотяpersist
эти запросы могут и не понадобиться.D4. Бывают случаи, когда вы просто не доверяете коду, который вызывает ваш код, и чтобы убедиться, что данные не обновляются, а вставляются, вы должны их использовать
persist
.источник
Я получал исключения lazyLoading для моей сущности, потому что пытался получить доступ к загруженной ленивой коллекции, которая находилась в сеансе.
То, что я сделал бы, было в отдельном запросе, получить сущность из сеанса и затем попытаться получить доступ к коллекции на моей странице JSP, которая была проблематичной.
Чтобы облегчить это, я обновил ту же сущность в моем контроллере и передал ее моему jsp, хотя я представляю себе, когда я повторно сохраняю в сеансе, что он также будет доступен, хотя
SessionScope
и не выбрасываетLazyLoadingException
, модификацию примера 2:Следующее сработало для меня:
источник
Я нашел это объяснение в документах Hibernate, потому что они содержат пример использования:
От: http://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/objectstate.html
источник
Просматривая ответы, не хватает некоторых деталей относительно «Каскад» и генерации идентификатора. См вопрос
Также стоит отметить, что вы можете иметь отдельные
Cascade
аннотации для слияния и сохранения:Cascade.MERGE
иCascade.PERSIST
которые будут обрабатываться в соответствии с используемым методом.Спекуляция твой друг;)
источник
Постоянные сущности
В отличие от метода слияния, метод persist довольно прост и интуитивен. Наиболее распространенный сценарий использования метода persist можно описать следующим образом:
«Вновь созданный экземпляр класса сущностей передается методу persist. После возврата из этого метода сущность управляется и планируется для вставки в базу данных. Это может произойти во время или до совершения транзакции, или когда вызывается метод flush. Если объект ссылается на другой объект посредством отношения, помеченного каскадной стратегией PERSIST, эта процедура также применяется к нему ".
Спецификация более детальна, однако, помнить их не важно, поскольку эти детали охватывают более или менее экзотические ситуации.
Слияние сущностей
По сравнению с сохранением, описание поведения слияния не так просто. Здесь нет основного сценария, как в случае с постоянным, и программист должен помнить все сценарии, чтобы написать правильный код. Мне кажется, что разработчики JPA хотели иметь какой-то метод, основной задачей которого была бы обработка отсоединенных сущностей (в отличие от метода persist, который в первую очередь имеет дело с вновь создаваемыми сущностями). Основная задача метода слияния состоит в передаче состояния из неуправляемый объект (переданный в качестве аргумента) своему управляемому аналогу в контексте постоянства. Эта задача, однако, делится далее на несколько сценариев, которые ухудшают разборчивость поведения метода в целом.
Вместо того, чтобы повторять абзацы из спецификации JPA, я подготовил блок-схему, которая схематически изображает поведение метода слияния:
Итак, когда я должен использовать постоянный и когда объединить?
упорствовать
слияние
источник
Сценарий X:
Таблица: Spitter (Один), Таблица: Spittles (Многие) (Spittles является владельцем отношений с FK: spitter_id)
Этот сценарий приводит к сохранению: Spitter и обоих Spittles, как если бы они принадлежали Same Spitter.
Сценарий Y:
Это сохранит Spitter, сохранит 2 Spittles, но они не будут ссылаться на тот же Spitter!
источник
Еще одно наблюдение:
merge()
будет заботиться об автоматически сгенерированном идентификаторе (проверено наIDENTITY
иSEQUENCE
), когда запись с таким идентификатором уже существует в вашей таблице. В таком случаеmerge()
постараюсь обновить запись. Однако, если идентификатор отсутствует или не совпадает ни с одной из существующих записей, онmerge()
будет полностью проигнорирован и попросит БД выделить новую. Иногда это является источником множества ошибок. Не используйтеmerge()
для принудительной идентификации для новой записи.persist()
с другой стороны, вы никогда не позволите вам даже передать ему идентификатор. Это немедленно провалится. В моем случае это:hibernate-jpa javadoc имеет подсказку:
источник
persist()
не будет жаловаться, что у него есть идентификатор, он только будет жаловаться, когда что-то с таким же идентификатором уже есть в базе данных.Возможно, вы пришли сюда за советом о том, когда использовать persist и когда использовать merge . Я думаю, что это зависит от ситуации: насколько вероятно, что вам нужно создать новую запись, и насколько сложно получить постоянные данные.
Предположим, вы можете использовать натуральный ключ / идентификатор.
Данные должны быть сохранены, но время от времени существует запись и требуется обновление. В этом случае вы можете попробовать персистирование, и если оно выдает исключение EntityExistsException, вы ищите его и объединяете данные:
try {entityManager.persist (entity)}
catch (исключение EntityExistsException) {/ * получить и объединить * /}
Сохраняемые данные необходимо обновить, но время от времени для этих данных еще нет записей. В этом случае вы ищете его и сохраните, если объект отсутствует:
entity = entityManager.find (ключ);
if (entity == null) {entityManager.persist (entity); }
еще {/ * объединить * /}
Если у вас нет естественного ключа / идентификатора, вам будет сложнее выяснить, существует сущность или нет, или как ее найти.
Слияния можно обрабатывать двумя способами:
источник
persist (entity) должен использоваться с абсолютно новыми сущностями, чтобы добавить их в БД (если сущность уже существует в БД, будет выброшено исключение EntityExistsException).
Слияние (сущность) должно использоваться, чтобы вернуть сущность обратно в контекст постоянства, если сущность была отсоединена и была изменена.
Вероятно, persist генерирует оператор INSERT SQL и объединяет оператор UPDATE SQL (но я не уверен).
источник