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

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

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

Комментарии к вопросу (2)
Решение

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

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

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.

Комментарии (12)

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

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

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

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

слияние:

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

упорствовать () эффективность:

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

упорствовать () семантика:

  • Убедитесь, что вы вставляете и не обновляете по ошибке.

Пример:

{
    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 () может быть настолько же эффективным, насколько и result (), используя вызов INSERT с опцией ON DUPLICATE KEY UPDATE, JPA - это программирование очень высокого уровня, и вы не можете предполагать, что это будет иметь место везде ,.

Комментарии (4)

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

Кроме того, называть слияние для управляемых объектов также является ошибкой, поскольку управляемые объекты автоматически управляются Hibernate, а их состояние синхронизируется с записью базы данных [грязным механизмом проверки](https://vladmihalcea.com/the-checkanatomy of-hibern.

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

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

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

Во-первых, мы должны определить все состояния сущности:

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

Вновь созданный объект, который никогда не был связан с спящей «Сессией» (a.k.a «Контекст постоянства») и не сопоставлен ни с одной строкой таблицы базы данных, считается находящимся в состоянии «Новый (переходный)».

Чтобы стать настойчивым, нам нужно либо явно вызвать метод «EntityManager # persist», либо использовать механизм транзитивного постоянства.

  • Постоянный (управляемый)

    Постоянный объект был связан со строкой таблицы базы данных, и он управляется текущим запущенным контекстом постоянства. Любые изменения, внесенные в такой объект, будут обнаружены и распространены в базе данных (во время выполнения сеанса). С Hibernate нам больше не нужно выполнять операторы INSERT / UPDATE / DELETE. Hibernate использует рабочий стиль транзакции write-behind, и изменения синхронизируются в самый последний ответственный момент, в течение текущего времени сброса Session.

  • Отдельно

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

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

- Присоединение

 Hibernate (но не JPA 2.1) поддерживает повторное прикрепление с помощью метода Session # update.
 Hibernate Session может связывать только один объект Entity для данной строки базы данных. Это связано с тем, что контекст устойчивости действует как кэш в памяти (кэш первого уровня), и только одно значение (субъект) связано с данным ключом (тип объекта и идентификатор базы данных).
 Объект может быть повторно присоединен только в том случае, если нет другого объекта JVM (соответствующего той же строке базы данных), уже связанного с текущей сессией Hibernate.
  • Слияние

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

  • Удалено

Хотя JPA требует, чтобы удалялись только управляемые объекты, Hibernate также может удалять отдельные объекты (но только с помощью вызова метода Session # delete). Удаленный объект запланирован только для удаления, а фактический оператор базы данных DELETE будет выполнен во время сброса сеанса.

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

введите описание изображения здесь! Или, если вы используете Hibernate специфический API:

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

Комментарии (9)

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

Комментарии (2)

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

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

Таким образом, использование persist () будет подходящим, когда объект не должен быть отдельным объектом. Вы можете предпочесть, чтобы код выбрасывал «PersistenceException», чтобы он быстро выпадал.

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

Комментарии (3)

Есть еще некоторые различия между 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. Если вы удаляете сущность и затем решаете сохранить сущность обратно, вы можете сделать это только с помощью resist (), потому что «merge» бросит «IllegalArgumentException».

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

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

Комментарии (0)

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

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

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

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

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

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

Комментарии (0)

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

Что бы я сделал, так это в отдельном запросе, извлек сущность из сеанса, а затем попытался получить доступ к коллекции на моей странице 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!!
Комментарии (0)

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

Постоянные лица

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

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

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

Объединение организаций

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

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

Итак, когда я должен использовать упорствовать и когда слияние?

persist

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

merge

  • Вы хотите, чтобы метод вставлял или обновлял объект в базе данных.
  • Вы хотите обрабатывать объекты без гражданства (объекты передачи данных в сервисах)
  • Вы хотите вставить новую сущность, которая может иметь ссылку на другую сущность, которая может, но не может быть создана еще (отношения должны быть помечены как MERGE). Например, вставка новой фотографии со ссылкой на новый или уже существующий альбом.
Комментарии (1)

Просматривая ответы, отсутствуют некоторые детали, касающиеся «Каскада» и генерации идентификаторов. [См. Вопрос][1]

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

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

[1]: https://stackoverflow.com/questions/17816196/jpa-how-can-i-get-the-generated-id-object-when-using-merge-from-parent-but-child/17830782 # 17830782

Комментарии (0)

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

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

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

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

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

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

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

Комментарии (0)

Сценарий X:

Таблица: Спиттер (один), Таблица: Спитлз (многие) (Спитлз является владельцем отношений с FK: spitter_id)

Этот сценарий приводит к сохранению: Spitter и оба 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.addSpittle(spittle3); // 
Комментарии (4)

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

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

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

Вызывается: org.hibernate.PersistentObjectException: отдельная сущность перешел, чтобы упорствовать

Хибернате-Джпа Джавадок имеет подсказку:

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

Комментарии (1)

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

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

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

    попробуйте {entityManager.persist (entity)}

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

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

    сущность = сущностьManager.find (ключ) ;

    if (субъект == null) {entityManager.persist (субъект); }

    else {/ merge /}

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

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

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

resist (entity) следует использовать с совершенно новыми объектами, чтобы добавить их в DB (если объект уже существует в DB, будет EntityExistsException drow).

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

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

Комментарии (3)