Дополнительно
JPA EntityManager: зачем использовать result () над merge ()?
EntityManager.merge ()
может вставлять новые объекты и обновлять существующие.
Почему нужно использовать persist ()
(который может создавать только новые объекты)?
920
15
В любом случае добавит сущность в PersistenceContext, разница в том, что вы делаете с сущностью впоследствии.
Persist берет экземпляр сущности, добавляет его в контекст и управляет этим экземпляром (т. Е. Будут отслеживаться будущие обновления объекта).
Merge создает новый экземпляр вашей организации, копирует состояние из поставляемой организации и управляет новой копией. Экземпляр, который вы передаете, не будет управляться (любые изменения, которые вы вносите, не будут частью транзакции - если вы не вызовете слияние снова).
Может быть, пример кода поможет.
Сценарии 1 и 3 примерно эквивалентны, но есть некоторые ситуации, когда вы хотите использовать Сценарий 2.
Поощрение и слияние предназначены для двух разных целей (они вовсе не альтернативы).
(под редакцией для расширения информации о различиях)
упорствовать:
слияние:
упорствовать () эффективность:
упорствовать () семантика:
Пример:
Таким образом, существует только 1 прикрепленный объект для любого реестра в менеджере сущностей.
merge () для объекта с идентификатором - это что-то вроде:
Хотя если подключен к MySQL merge () может быть настолько же эффективным, насколько и result (), используя вызов INSERT с опцией ON DUPLICATE KEY UPDATE, JPA - это программирование очень высокого уровня, и вы не можете предполагать, что это будет иметь место везде ,.
Если вы используете назначенный генератор, использование слияния вместо сохранения может вызвать избыточный оператор 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
.Отдельно
После закрытия текущего текущего контекста сохранения все ранее управляемые объекты становятся отделенными. Последовательные изменения больше не будут отслеживаться, и автоматическая синхронизация базы данных не произойдет.
Чтобы связать отдельное лицо с активным сеансом спящего режима, вы можете выбрать один из следующих вариантов:
Слияние собирается скопировать состояние обособленного объекта (источник) в экземпляр управляемого объекта (назначение). Если объект слияния не имеет эквивалента в текущей сессии, он будет извлечен из базы данных. Экземпляр отдельного объекта будет оставаться отсоединенным даже после операции слияния.
Хотя JPA требует, чтобы удалялись только управляемые объекты, Hibernate также может удалять отдельные объекты (но только с помощью вызова метода Session # delete). Удаленный объект запланирован только для удаления, а фактический оператор базы данных DELETE будет выполнен во время сброса сеанса.
Чтобы лучше понять переходы состояний JPA, вы можете визуализировать следующую диаграмму:
введите описание изображения здесь! Или, если вы используете Hibernate специфический API:
введите описание изображения здесь!
Я заметил, что когда я использовал
em.merge
, я получал операторSELECT
для каждогоINSERT``, даже когда не было поля, которое JPA генерировал для меня - поле первичного ключа было UUID, которое я установил сам ,. Я переключился на
em.persist (myEntityObject)и получил только заявления
INSERT`.Спецификация JPA говорит следующее о
persist ()
.Таким образом, использование
persist ()
будет подходящим, когда объект не должен быть отдельным объектом. Вы можете предпочесть, чтобы код выбрасывал «PersistenceException», чтобы он быстро выпадал.Хотя спецификация неясна,
persist ()
может установить@ GeneratedValue``
@ Idдля объекта.
merge ()однако должен иметь объект с уже сгенерированным
@ Id`.Есть еще некоторые различия между
merge
и ` persist '(я перечислю еще раз те, которые уже размещены здесь):D1.
merge
не управляет переданным объектом, а возвращает другой управляемый экземпляр.persist
с другой стороны сделает управляемую переданную сущность:D2. Если вы удаляете сущность и затем решаете сохранить сущность обратно, вы можете сделать это только с помощью resist (), потому что «merge» бросит «IllegalArgumentException».
D3. Если вы решили позаботиться вручную о своих идентификаторах (например, с помощью UUID), то «merge» операция вызовет последующие запросы
SELECT
, чтобы искать существующие объекты с этим идентификатором, в то время какpersist
может не нуждаться в этих запросах.D4. Есть случаи, когда вы просто не доверяете коду, который вызывает ваш код, и чтобы убедиться, что данные не обновляются, а скорее вставляются, вы должны использовать «persist».
Еще некоторые подробности о слиянии, которые помогут вам использовать слияние поверх result:
Вся приведенная выше информация была взята из «Pro JPA 2 Mastering Java ™ Persistence API» Майком Китом и Мерриком Шникариолом. Глава 6. Секция отряда и слияния. Эта книга на самом деле является второй книгой, посвященной JPA авторами. Эта новая книга содержит много новой информации, чем предыдущая. Я действительно рекомендовал прочитать эту книгу для тех, кто будет серьезно связан с JPA. Я прошу прощения за анонимную публикацию моего первого ответа.
Я получал ленивые исключения для загрузки своей сущности, потому что пытался получить доступ к ленивой загруженной коллекции, которая была в сеансе.
Что бы я сделал, так это в отдельном запросе, извлек сущность из сеанса, а затем попытался получить доступ к коллекции на моей странице jsp, что было проблематично.
Чтобы облегчить это, я обновил ту же сущность в своем контроллере и передал ее своему jsp, хотя я представляю, когда я заново сохраняю сеанс, что он также будет доступен через «SessionScope» и не будет бросать «LazyLoadingException», модификацию пример 2:
Следующее сработало для меня:
Постоянные лица
В отличие от метода слияния метод resistance довольно прост и интуитивно понятен. Наиболее распространенный сценарий использования метода preseve можно обобщить следующим образом:
«Недавно созданный экземпляр класса сущностей передается методу presist. После возвращения этого метода объект управляется и планируется для вставки в базу данных. Это может произойти во время или до совершения транзакции или при вызове метода сброса. Если объект ссылается на другой объект через отношения, отмеченные каскадной стратегией PERSIST, эта процедура также применяется к нему.«
Спецификация более детально вписывается, однако запоминание их не имеет решающего значения, поскольку эти детали охватывают только более или менее экзотические ситуации.
Объединение организаций
По сравнению с сохранением, описание поведения слияния не так просто. Не существует основного сценария, как в случае сохранения, и программист должен запомнить все сценарии, чтобы написать правильный код. Мне кажется, что дизайнеры JPA хотели иметь какой-то метод, основной задачей которого было бы обращение с отдельными объектами (как противоположность методу сохранения, который в первую очередь касается вновь созданных объектов.Основная задача метода слияния заключается в передаче состояния из неуправляемого объекта (переданного в качестве аргумента) его управляемому аналогу в контексте сохранения. Эта задача, однако, далее делится на несколько сценариев, которые ухудшают понятность поведения общего метода.
Вместо повторения абзацев из спецификации JPA я подготовил блок-схему, которая схематически отображает поведение метода слияния:
Итак, когда я должен использовать упорствовать и когда слияние?
persist
merge
Просматривая ответы, отсутствуют некоторые детали, касающиеся «Каскада» и генерации идентификаторов. [См. Вопрос][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
Я нашел это объяснение из документов Hibernate поучительным, потому что они содержат пример использования:
От: http://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/objectstate.html
Сценарий X:
Таблица: Спиттер (один), Таблица: Спитлз (многие) (Спитлз является владельцем отношений с FK: spitter_id)
Этот сценарий приводит к сохранению: Spitter и оба Spittles, как если бы они принадлежали одному Spitter.
Еще одно наблюдение:
merge ()
будет заботиться только об автоматически сгенерированном идентификаторе (проверено наIDENTITY
иSEQUENCE
), когда запись с таким идентификатором уже существует в вашей таблице. В этом случаеmerge ()
попытается обновить запись. Однако, если идентификатор отсутствует или не соответствует существующим записям,merge ()
полностью проигнорирует его и попросит db выделить новую. Иногда это источник множества ошибок. Не используйтеmerge ()
, чтобы принудительно установить идентификатор для новой записи.persist ()
с другой стороны, никогда не позволит вам даже передать идентификатор ему. Это немедленно потерпит неудачу. В моем случае это:Хибернате-Джпа Джавадок имеет подсказку:
Возможно, вы пришли сюда за советом, когда использовать persist и когда использовать merge . Я думаю, что это зависит от ситуации: насколько вероятно, что вам нужно создать новую запись и насколько сложно получить достоверные данные.
Давайте предположим, что вы можете использовать естественный ключ / идентификатор.
Данные должны быть сохранены, но время от времени существует запись и требуется обновление. В этом случае вы можете попробовать упорство, и если оно выдает EntityExistsException, вы просматриваете его и объединяете данные:
попробуйте {entityManager.persist (entity)}
catch (EntityExistsException исключение) {/ извлечь и объединить /}
Постоянные данные необходимо обновлять, но время от времени записи для данных пока нет. В этом случае вы просматриваете его и делаете упорство, если объект отсутствует:
сущность = сущностьManager.find (ключ) ;
if (субъект == null) {entityManager.persist (субъект); }
else {/ merge /}
Если у вас нет естественного ключа / идентификатора, вам будет сложнее выяснить, существует ли сущность или нет, или как ее искать.
С слияниями можно бороться и двумя способами:
resist (entity) следует использовать с совершенно новыми объектами, чтобы добавить их в DB (если объект уже существует в DB, будет EntityExistsException drow).
merge (существо) следует использовать, чтобы вернуть сущность к контексту сохранения, если сущность была отделена и была изменена.
Вероятно, упорствует генерирует оператор INSERT sql и объединяет оператор UPDATE sql (но я не уверен).