JPA EntityManager: зачем использовать persist () вместо merge ()?

951

EntityManager.merge() может вставлять новые объекты и обновлять существующие.

Почему один хочет использовать persist()(который может создавать только новые объекты)?

Аарон Дигулла
источник
13
techblog.bozho.net/?p=266 связанный
Божо
2
Если вам нравятся диаграммы. См. Это: spitballer.blogspot.in/2010/04/…
RBz

Ответы:

1615

В любом случае вы добавите объект в PersistenceContext, разница в том, что вы будете делать с объектом позже.

Persist берет экземпляр объекта, добавляет его в контекст и управляет этим экземпляром (т.е. будущие обновления объекта будут отслеживаться).

Слияние возвращает управляемый экземпляр, в который было объединено состояние. Он возвращает то, что существует в PersistenceContext, или создает новый экземпляр вашей сущности. В любом случае он скопирует состояние из предоставленного объекта и вернет управляемую копию. Экземпляр, который вы передаете, не будет управляемым (любые внесенные вами изменения не будут частью транзакции - если вы не вызовете merge снова). Вы можете через использование возвращенного экземпляра (управляемого).

Может быть, пример кода поможет.

MyEntity e = new MyEntity();

// scenario 1
// tran starts
em.persist(e); 
e.setSomeField(someValue); 
// tran ends, and the row for someField is updated in the database

// scenario 2
// tran starts
e = new MyEntity();
em.merge(e);
e.setSomeField(anotherValue); 
// tran ends but the row for someField is not updated in the database
// (you made the changes *after* merging)

// scenario 3
// tran starts
e = new MyEntity();
MyEntity e2 = em.merge(e);
e2.setSomeField(anotherValue); 
// tran ends and the row for someField is updated
// (the changes were made to e2, not e)

Сценарии 1 и 3 примерно эквивалентны, но в некоторых ситуациях вы хотите использовать сценарий 2.

Майк
источник
3
@dma_k: похоже, вы используете Hibernate. Я менее знаком с Hibernate, чем JPA - но в JPA, если вы вызовете EntityManager.persist () и передадите отдельную сущность, вы: a) немедленно получите EntityExistsException или b) получите другое PersistenceException во время сброса / фиксации. Может быть, я неправильно понял вопрос здесь?
Майк
49
Этот ответ мог бы быть улучшен, если бы он также охватывал случаи, когда объединяемый / сохраняемый объект уже существует в постоянном контексте (или, по крайней мере, прояснял, что он описывает поведение только тогда, когда сохраненный / объединенный объект еще не существует)
Генри
2
Один метод более эффективен? Возможно merge, полная копия объекта, прежде чем управлять им, снижает производительность?
Кевин Мередит
2
А как насчет идентификаторов? Если у меня есть, @GeneratedIdя могу получить это в сценарии 2?
Расио
7
Майк: «Слияние создает новый экземпляр ...»: это не всегда так. Если EntityManager находит уже управляемый объект в своем контексте, он возвращает этот экземпляр (после обновления полей). Пожалуйста, отредактируйте свой ответ, тогда я буду голосовать за него.
Хери
181

Persist и merge предназначены для двух разных целей (они вовсе не являются альтернативами).

(отредактировано для расширения информации о различиях)

сохраняются:

  • Вставить новый регистр в базу данных
  • Прикрепите объект к менеджеру сущностей.

слияния:

  • Найдите прикрепленный объект с тем же идентификатором и обновите его.
  • Если существует, обновите и верните уже прикрепленный объект.
  • Если не существует, вставьте новый регистр в базу данных.

Persist () эффективность:

  • Это может быть более эффективным для вставки нового регистра в базу данных, чем merge ().
  • Это не дублирует оригинальный объект.

Семантика persist ():

  • Это гарантирует, что вы вставляете, а не обновляете по ошибке.

Пример:

{
    AnyEntity newEntity;
    AnyEntity nonAttachedEntity;
    AnyEntity attachedEntity;

    // Create a new entity and persist it        
    newEntity = new AnyEntity();
    em.persist(newEntity);

    // Save 1 to the database at next flush
    newEntity.setValue(1);

    // Create a new entity with the same Id than the persisted one.
    AnyEntity nonAttachedEntity = new AnyEntity();
    nonAttachedEntity.setId(newEntity.getId());

    // Save 2 to the database at next flush instead of 1!!!
    nonAttachedEntity.setValue(2);
    attachedEntity = em.merge(nonAttachedEntity);

    // This condition returns true
    // merge has found the already attached object (newEntity) and returns it.
    if(attachedEntity==newEntity) {
            System.out.print("They are the same object!");
    }

    // Set 3 to value
    attachedEntity.setValue(3);
    // Really, now both are the same object. Prints 3
    System.out.println(newEntity.getValue());

    // Modify the un attached object has no effect to the entity manager
    // nor to the other objects
    nonAttachedEntity.setValue(42);
}

Таким образом, существует только 1 прикрепленный объект для любого регистра в менеджере сущностей.

merge () для сущности с идентификатором выглядит примерно так:

AnyEntity myMerge(AnyEntity entityToSave) {
    AnyEntity attached = em.find(AnyEntity.class, entityToSave.getId());
    if(attached==null) {
            attached = new AnyEntity();
            em.persist(attached);
    }
    BeanUtils.copyProperties(attached, entityToSave);

    return attached;
}

Хотя при подключении к MySQL функция merge () может быть столь же эффективной, как и persist () с использованием вызова INSERT с опцией ON DUPLICATE KEY UPDATE, JPA - это программирование очень высокого уровня, и вы не можете предполагать, что это будет иметь место везде.

Хосеп Панадеро
источник
Можете ли вы назвать случай , когда это не действует , чтобы заменить em.persist(x)с x = em.merge(x)?
Аарон Дигулла
20
persist () может вызвать исключение EntityExistsException. Если вы хотите быть уверены, что ваш код выполняет вставку, а не обновление данных, вы должны использовать persist.
Хосеп Панадеро
1
merge()может также броситьEntityExistsException
Шон
1
@ Нет. Может, потому что это так RuntimeException, но это не упоминается в Javadoc.
Мартин
154

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

Кроме того, вызов слияния для управляемых объектов также является ошибкой, поскольку Hibernate автоматически управляет управляемыми объектами, и их состояние синхронизируется с записью базы данных с помощью механизма грязной проверки после сброса контекста постоянства .

Чтобы понять, как все это работает, вы должны сначала знать, что Hibernate переключает мышление разработчика с операторов SQL на переходы состояний сущностей .

Когда Hibernate активно управляет сущностью, все изменения будут автоматически распространяться в базу данных.

Hibernate отслеживает подключенные объекты. Но для того, чтобы субъект стал управляемым, он должен находиться в правильном состоянии.

Чтобы лучше понять переходы состояния JPA, вы можете представить следующую диаграмму:

Переходы состояния объекта JPA

Или, если вы используете специальный API Hibernate:

Переходы из состояния гибернации в сущность

Как показано на приведенных выше диаграммах, объект может находиться в одном из следующих четырех состояний:

  • Новый (Переходный)

    Вновь созданный объект, который никогда не был связан с Hibernate Session(aka Persistence 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 базы данных будет выполнен во время сброса сеанса.

Влад Михалча
источник
Вы можете посмотреть stackoverflow.com/questions/46214322/… ?
gstackoverflow
@gstackoverflow Ответ, который вы получили, правильный. Для получения более подробной информации, ознакомьтесь с этой статьей или моей книгой, High-Performance Java Persistence .
Влад Михалча
Таким образом, нет возможности изменить порядок работы для orphanremoval = true?
gstackoverflow
Ваша статья о порядке работы в обычном случае. Мой вопрос, специфичный для orphanRemoval
gstackoverflow
1
Дело в том, что с помощью такой диаграммы невозможно объяснить спящий режим. Почему вы не можете очистить сеанс после отсоединения? Что происходит, когда вы пытаетесь сохранить уже сохраненную сущность? Почему это поведение сброса отличается, когда речь идет о сохранении и сохранении? Есть 1000 таких вопросов, к которым никто не имеет четкой логики.
GingerBeer
37

Я заметил, что когда я использовал em.merge, я получал SELECTутверждение для каждого INSERT, даже когда не было никакого поля, которое JPA генерировал для меня - поле первичного ключа было UUID, который я установил сам. Я переключился на em.persist(myEntityObject)и получил только INSERTзаявления тогда.

Сара Весселс
источник
3
Имеет смысл, так как вы назначаете идентификаторы, а контейнер JPA не знает, откуда вы это взяли. Существует (небольшая) вероятность того, что объект уже существует в базе данных, например, в случае, когда несколько приложений пишут в одну и ту же базу данных.
Аарон Дигулла
Я сталкивался с подобной проблемой merge(). У меня была база данных PostgreSQL со сложным представлением : представление агрегированных данных из нескольких таблиц (таблицы имели одинаковую структуру, но разные имена). Так что JPA попытался сделать merge(), но на самом деле JPA сначала сделал SELECT(база данных из-за настроек представления может вернуть несколько записей с одним и тем же первичным ключом из разных таблиц!), Затем JPA (Hibernate был реализацией) не удалось: есть несколько записей с одним и тем же ключом ( org.hibernate.HibernateException: More than one row with the given identifier was found). В моем случае persist()мне помогли.
flaz14
29

В спецификации JPA говорится следующее persist().

Если X - это отдельный объект, он EntityExistsExceptionможет быть сгенерирован, когда вызывается операция persist, или тот EntityExistsExceptionили иной объект может быть сгенерирован во время сброса PersistenceExceptionили фиксации.

Поэтому использование persist()будет целесообразным, когда объект не должен быть отдельным объектом. Вы можете предпочесть, чтобы код выдавал код, PersistenceExceptionчтобы он быстро завершался с ошибкой.

Хотя спецификация неясна , persist()может установить @GeneratedValue @Idдля объекта. merge()однако должен иметь объект с @Idуже сгенерированным.

Raedwald
источник
5
+1 за " merge()однако должен иметь объект с @Id уже сгенерированным . " Всякий раз, когда EntityManager не находит значение для поля идентификатора объекта, оно сохраняется (вставляется) в БД.
Омар
Сначала я не поняла этого, так как не знала, в каких штатах. Надеюсь, это поможет кому-то, как и мне. docs.jboss.org/hibernate/core/3.6/reference/en-US/html/…
RBz
1
@GeneratedValue не имеет никакого другого значения для merge () и
persist
17

Еще некоторые подробности о слиянии, которые помогут вам использовать слияние по-прежнему:

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

Когда merge () вызывается для новой сущности, она ведет себя подобно операции persist (). Он добавляет сущность в контекст постоянства, но вместо добавления исходного экземпляра сущности создает новую копию и управляет этим экземпляром. Копия, созданная операцией merge (), сохраняется, как если бы для нее был вызван метод persist ().

При наличии взаимосвязей операция merge () попытается обновить управляемый объект, чтобы он указывал на управляемые версии объектов, на которые ссылается отдельный объект. Если у сущности есть отношение к объекту, который не имеет постоянной идентичности, результат операции слияния не определен. Некоторые поставщики могут разрешать управляемой копии указывать на непостоянный объект, тогда как другие могут немедленно генерировать исключение. Операция merge () может быть опционально каскадной в этих случаях, чтобы исключить возникновение исключения. Мы расскажем о каскадировании операции merge () позже в этом разделе. Если объединяемая сущность указывает на удаленную сущность, возникает исключение IllegalArgumentException.

Отношения отложенной загрузки являются особым случаем в операции слияния. Если отношение отложенной загрузки не было запущено на объекте до его отсоединения, то это отношение будет игнорироваться при объединении объекта. Если связь была запущена во время управления, а затем была установлена ​​на ноль, пока сущность была отсоединена, управляемая версия сущности также будет очищена во время слияния ».

Вся вышеупомянутая информация была взята из «Pro JPA 2 Mastering Java ™ Persistence API» Майка Кейта и Меррика Шникариола. Глава 6. Раздел отрыв и слияние. Эта книга на самом деле вторая книга, посвященная JPA авторами. Эта новая книга имеет много новой информации, чем прежняя. Я действительно рекомендовал прочитать эту книгу для тех, кто будет серьезно связан с JPA. Прошу прощения за анонимную публикацию моего первого ответа.

Хуршед Салимов
источник
17

Есть еще несколько различий между mergeи persist(я снова перечислю те, что были размещены здесь):

D1. mergeне делает переданный объект управляемым, а возвращает другой управляемый экземпляр. persistс другой стороны сделаем переданный объект управляемым:

//MERGE: passedEntity remains unmanaged, but newEntity will be managed
Entity newEntity = em.merge(passedEntity);

//PERSIST: passedEntity will be managed after this
em.persist(passedEntity);

D2. Если вы удалите сущность, а затем решите сохранить ее обратно, вы можете сделать это только с помощью persist (), потому что mergeбудет выброшено IllegalArgumentException.

D3. Если вы решили позаботиться о своих идентификаторах вручную (например, с помощью идентификаторов UUID), то merge операция будет инициировать последующие SELECTзапросы для поиска существующих объектов с таким идентификатором, хотя persistэти запросы могут и не понадобиться.

D4. Бывают случаи, когда вы просто не доверяете коду, который вызывает ваш код, и чтобы убедиться, что данные не обновляются, а вставляются, вы должны их использовать persist.

Андрей I
источник
8

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

То, что я сделал бы, было в отдельном запросе, получить сущность из сеанса и затем попытаться получить доступ к коллекции на моей странице JSP, которая была проблематичной.

Чтобы облегчить это, я обновил ту же сущность в моем контроллере и передал ее моему jsp, хотя я представляю себе, когда я повторно сохраняю в сеансе, что он также будет доступен, хотя SessionScopeи не выбрасывает LazyLoadingException, модификацию примера 2:

Следующее сработало для меня:

// scenario 2 MY WAY
// tran starts
e = new MyEntity();
e = em.merge(e); // re-assign to the same entity "e"

//access e from jsp and it will work dandy!!
logixplayer
источник
7

Я нашел это объяснение в документах Hibernate, потому что они содержат пример использования:

Использование и семантика merge (), кажется, вводит в заблуждение новых пользователей. Во-первых, до тех пор, пока вы не пытаетесь использовать состояние объекта, загруженное в одном диспетчере сущностей в другом новом диспетчере сущностей, вам вообще не нужно использовать merge () . Некоторые целые приложения никогда не будут использовать этот метод.

Обычно merge () используется в следующем сценарии:

  • Приложение загружает объект в первый менеджер сущностей
  • объект передается на уровень представления
  • некоторые изменения сделаны для объекта
  • объект передается обратно на уровень бизнес-логики
  • приложение сохраняет эти изменения, вызывая merge () во втором менеджере сущностей

Вот точная семантика слияния ():

  • если есть управляемый экземпляр с тем же идентификатором, который в настоящее время связан с контекстом постоянства, скопируйте состояние данного объекта в управляемый экземпляр
  • если в настоящее время нет управляемого экземпляра, связанного с контекстом постоянства, попробуйте загрузить его из базы данных или создать новый управляемый экземпляр
  • управляемый экземпляр возвращается
  • данный экземпляр не становится связанным с постоянным контекстом, он остается отсоединенным и обычно отбрасывается

От: http://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/objectstate.html

Рэй Хулха
источник
6

Просматривая ответы, не хватает некоторых деталей относительно «Каскад» и генерации идентификатора. См вопрос

Также стоит отметить, что вы можете иметь отдельные Cascadeаннотации для слияния и сохранения: Cascade.MERGEи Cascade.PERSISTкоторые будут обрабатываться в соответствии с используемым методом.

Спекуляция твой друг;)

Иоаннис Делигианнис
источник
6

JPA, бесспорно, большое упрощение в области корпоративных приложений, построенных на платформе Java. Как разработчик, который должен был справиться со сложностями старых компонентов управления данными в J2EE, я считаю включение JPA в спецификации Java EE большим шагом вперед. Однако, углубляясь в детали JPA, я нахожу вещи, которые не так просты. В этой статье я имею дело со сравнением методов слияния и сохранения EntityManager, чье перекрывающееся поведение может вызвать путаницу не только у новичка. Кроме того, я предлагаю обобщение, которое рассматривает оба метода как частные случаи объединения более общего метода.

Постоянные сущности

В отличие от метода слияния, метод persist довольно прост и интуитивен. Наиболее распространенный сценарий использования метода persist можно описать следующим образом:

«Вновь созданный экземпляр класса сущностей передается методу persist. После возврата из этого метода сущность управляется и планируется для вставки в базу данных. Это может произойти во время или до совершения транзакции, или когда вызывается метод flush. Если объект ссылается на другой объект посредством отношения, помеченного каскадной стратегией PERSIST, эта процедура также применяется к нему ".

введите описание изображения здесь

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

Слияние сущностей

По сравнению с сохранением, описание поведения слияния не так просто. Здесь нет основного сценария, как в случае с постоянным, и программист должен помнить все сценарии, чтобы написать правильный код. Мне кажется, что разработчики JPA хотели иметь какой-то метод, основной задачей которого была бы обработка отсоединенных сущностей (в отличие от метода persist, который в первую очередь имеет дело с вновь создаваемыми сущностями). Основная задача метода слияния состоит в передаче состояния из неуправляемый объект (переданный в качестве аргумента) своему управляемому аналогу в контексте постоянства. Эта задача, однако, делится далее на несколько сценариев, которые ухудшают разборчивость поведения метода в целом.

Вместо того, чтобы повторять абзацы из спецификации JPA, я подготовил блок-схему, которая схематически изображает поведение метода слияния:

введите описание изображения здесь

Итак, когда я должен использовать постоянный и когда объединить?

упорствовать

  • Вы хотите, чтобы метод всегда создавал новую сущность и никогда не обновлял сущность. В противном случае метод генерирует исключение вследствие нарушения уникальности первичного ключа.
  • Пакетные процессы, обрабатывающие объекты в состоянии (см. Шаблон шлюза).
  • Оптимизация производительности

слияние

  • Вы хотите, чтобы метод либо вставлял, либо обновлял сущность в базе данных.
  • Вы хотите обрабатывать объекты без сохранения состояния (объекты передачи данных в сервисах)
  • Вы хотите вставить новую сущность, которая может иметь ссылку на другую сущность, которая может, но еще не может быть создана (связь должна быть помечена как MERGE). Например, вставка новой фотографии со ссылкой на новый или уже существующий альбом.
Амит Гуджаратхи
источник
В чем разница между управляемым E и содержит ли ПК управляемую версию E?
GingerBeer
5

Сценарий X:

Таблица: Spitter (Один), Таблица: Spittles (Многие) (Spittles является владельцем отношений с FK: spitter_id)

Этот сценарий приводит к сохранению: Spitter и обоих Spittles, как если бы они принадлежали Same Spitter.

        Spitter spitter=new Spitter();  
    Spittle spittle3=new Spittle();     
    spitter.setUsername("George");
    spitter.setPassword("test1234");
    spittle3.setSpittle("I love java 2");       
    spittle3.setSpitter(spitter);               
    dao.addSpittle(spittle3); // <--persist     
    Spittle spittle=new Spittle();
    spittle.setSpittle("I love java");
    spittle.setSpitter(spitter);        
    dao.saveSpittle(spittle); //<-- merge!!

Сценарий Y:

Это сохранит Spitter, сохранит 2 Spittles, но они не будут ссылаться на тот же Spitter!

        Spitter spitter=new Spitter();  
    Spittle spittle3=new Spittle();     
    spitter.setUsername("George");
    spitter.setPassword("test1234");
    spittle3.setSpittle("I love java 2");       
    spittle3.setSpitter(spitter);               
    dao.save(spittle3); // <--merge!!       
    Spittle spittle=new Spittle();
    spittle.setSpittle("I love java");
    spittle.setSpitter(spitter);        
    dao.saveSpittle(spittle); //<-- merge!!
Георгий Папатеодору
источник
1
Сплиттер - объект, взятый из книги «Весна в действии» третьего издания Грейга Уоллса. Spitters - это люди, которые говорят что-то, а их Spittle - это то, что они на самом деле говорят. Таким образом, у Spitter есть много spittles, что означает, что у него есть список строк.
Георгий Папатеодору
1
Вы могли бы использовать пример, который немного более читабелен, не читая Spring in Action ...
wonderb0lt
1
На самом деле вам не нужно знать, что такое плевок или сплиттер, так как наверху написано, что Спиттер - это таблица, спиттер - это еще одна таблица, которая владеет ... этим и этим ...
Джордж Папатеодору
3

Еще одно наблюдение:

merge()будет заботиться об автоматически сгенерированном идентификаторе (проверено на IDENTITYи SEQUENCE), когда запись с таким идентификатором уже существует в вашей таблице. В таком случае merge()постараюсь обновить запись. Однако, если идентификатор отсутствует или не совпадает ни с одной из существующих записей, он merge()будет полностью проигнорирован и попросит БД выделить новую. Иногда это является источником множества ошибок. Не используйте merge()для принудительной идентификации для новой записи.

persist()с другой стороны, вы никогда не позволите вам даже передать ему идентификатор. Это немедленно провалится. В моем случае это:

Вызывается: org.hibernate.PersistentObjectException: отдельная сущность передана для сохранения

hibernate-jpa javadoc имеет подсказку:

Выдает : javax.persistence.EntityExistsException - если объект уже существует. (Если объект уже существует, исключение EntityExistsException может быть выдано при вызове операции persist, либо исключение EntityExistsException или другое PersistenceException может быть выдано во время сброса или фиксации.)

yuranos
источник
2
Если вы не используете автоматически сгенерированные идентификаторы, вам придется вручную назначить новый идентификатор вашей сущности. persist()не будет жаловаться, что у него есть идентификатор, он только будет жаловаться, когда что-то с таким же идентификатором уже есть в базе данных.
HRS
1

Возможно, вы пришли сюда за советом о том, когда использовать persist и когда использовать merge . Я думаю, что это зависит от ситуации: насколько вероятно, что вам нужно создать новую запись, и насколько сложно получить постоянные данные.

Предположим, вы можете использовать натуральный ключ / идентификатор.

  • Данные должны быть сохранены, но время от времени существует запись и требуется обновление. В этом случае вы можете попробовать персистирование, и если оно выдает исключение EntityExistsException, вы ищите его и объединяете данные:

    try {entityManager.persist (entity)}

    catch (исключение EntityExistsException) {/ * получить и объединить * /}

  • Сохраняемые данные необходимо обновить, но время от времени для этих данных еще нет записей. В этом случае вы ищете его и сохраните, если объект отсутствует:

    entity = entityManager.find (ключ);

    if (entity == null) {entityManager.persist (entity); }

    еще {/ * объединить * /}

Если у вас нет естественного ключа / идентификатора, вам будет сложнее выяснить, существует сущность или нет, или как ее найти.

Слияния можно обрабатывать двумя способами:

  1. Если изменения обычно невелики, примените их к управляемому объекту.
  2. Если изменения являются общими, скопируйте идентификатор из сохраняемого объекта, а также неизмененные данные. Затем вызовите EntityManager :: merge () для замены старого содержимого.
Питер Виллемс
источник
0

persist (entity) должен использоваться с абсолютно новыми сущностями, чтобы добавить их в БД (если сущность уже существует в БД, будет выброшено исключение EntityExistsException).

Слияние (сущность) должно использоваться, чтобы вернуть сущность обратно в контекст постоянства, если сущность была отсоединена и была изменена.

Вероятно, persist генерирует оператор INSERT SQL и объединяет оператор UPDATE SQL (но я не уверен).

Krystian
источник
Это неверно Если вы вызываете merge (e) для нового e, его необходимо сохранить.
Педро Ламанао
@ PedroLamarão warren.chinalle.com/wp-content/uploads/2011/09/…
Кристиан
Из спецификации JPA версии 2.1, раздел 3.2.7.1, второй маркер: «Если X является новым экземпляром объекта, создается новый экземпляр управляемого объекта X ', и состояние X копируется в новый экземпляр управляемого объекта X'».
Педро Ламанао