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

885

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

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

techblog.bozho.net/?p=266 связанные с Bozho
Если вам нравятся диаграммы. Порекомендуйте это:spitballer.blogspot.in/2010/04/… RBz

Ваш Ответ

15   ответов
6

applications built on the Java platform. As a developer who had to cope up with the intricacies of the old entity beans in J2EE I see the inclusion of JPA among the Java EE specifications as a big leap forward. However, while delving deeper into the JPA details I find things that are not so easy. In this article I deal with comparison of the EntityManager’s merge and persist methods whose overlapping behavior may cause confusion not only to a newbie. Furthermore I propose a generalization that sees both methods as special cases of a more general method combine.

Persisting entities

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

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

enter image description here

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

Merging entities

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

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

enter image description here

So, when should I use persist and when merge?

persist

You want the method always creates a new entity and never updates an entity. Otherwise, the method throws an exception as a consequence of primary key uniqueness violation. Batch processes, handling entities in a stateful manner (see Gateway pattern). Performance optimization

merge

You want the method either inserts or updates an entity in the database. You want to handle entities in a stateless manner (data transfer objects in services) You want to insert a new entity that may have a reference to another entity that may but may not be created yet (relationship must be marked MERGE). For example, inserting a new photo with a reference to either a new or a preexisting album.
В чем разница между управляемым E и содержит ли ПК управляемую версию E?
160

яются альтернативами).

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

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

Insert a new register to the database Attach the object to the entity manager.

слияния:

Find an attached object with the same id and update it. If exists update and return the already attached object. If doesn't exist insert the new register to the database.

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

It could be more efficient for inserting a new register to a database than merge(). It doesn't duplicates the original object.

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

It makes sure that you are inserting and not updating by mistake.

Пример:

{
    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)? Aaron Digulla
merge() также может броситьEntityExistsException
persist () может вызвать исключение EntityExistsException. Если вы хотите быть уверены, что ваш код выполняет вставку, а не обновление данных, вы должны использовать persist.
5

потому что они содержат пример использования:

The usage and semantics of merge() seems to be confusing for new users. Firstly, as long as you are not trying to use object state loaded in one entity manager in another new entity manager, you should not need to use merge() at all. Some whole applications will never use this method.

Usually merge() is used in the following scenario:

The application loads an object in the first entity manager the object is passed up to the presentation layer some modifications are made to the object the object is passed back down to the business logic layer the application persists these modifications by calling merge() in a second entity manager

Here is the exact semantic of merge():

if there is a managed instance with the same identifier currently associated with the persistence context, copy the state of the given object onto the managed instance if there is no managed instance currently associated with the persistence context, try to load it from the database, or create a new managed instance the managed instance is returned the given instance does not become associated with the persistence context, it remains detached and is usually discarded

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

8

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

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

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

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

Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist

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

Throws: javax.persistence.EntityExistsException - if the entity already exists. (If the entity already exists, the EntityExistsException may be thrown when the persist operation is invoked, or the EntityExistsException or another PersistenceException may be thrown at flush or commit time.)

16

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

Returning a managed instance other than the original entity is a critical part of the merge process. If an entity instance with the same identifier already exists in the persistence context, the provider will overwrite its state with the state of the entity that is being merged, but the managed version that existed already must be returned to the client so that it can be used. If the provider did not update the Employee instance in the persistence context, any references to that instance will become inconsistent with the new state being merged in.

When merge() is invoked on a new entity, it behaves similarly to the persist() operation. It adds the entity to the persistence context, but instead of adding the original entity instance, it creates a new copy and manages that instance instead. The copy that is created by the merge() operation is persisted as if the persist() method were invoked on it.

In the presence of relationships, the merge() operation will attempt to update the managed entity to point to managed versions of the entities referenced by the detached entity. If the entity has a relationship to an object that has no persistent identity, the outcome of the merge operation is undefined. Some providers might allow the managed copy to point to the non-persistent object, whereas others might throw an exception immediately. The merge() operation can be optionally cascaded in these cases to prevent an exception from occurring. We will cover cascading of the merge() operation later in this section. If an entity being merged points to a removed entity, an IllegalArgumentException exception will be thrown.

Lazy-loading relationships are a special case in the merge operation. If a lazy-loading relationship was not triggered on an entity before it became detached, that relationship will be ignored when the entity is merged. If the relationship was triggered while managed and then set to null while the entity was detached, the managed version of the entity will likewise have the relationship cleared during the merge."

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

5

Таблица: 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!!
Что такое сплиттер?
Сплиттер - это объект, взятый из книги «Весна в действии». третье издание Грейг Уоллс. Spitters - это люди, которые говорят что-то, а их Spittle - это то, что они на самом деле говорят. Таким образом, у Spitter есть много spittles, что означает, что у него есть список строк.
Вы могли бы использовать пример, который немного более читабелен без чтения Spring in Action ...
Вам на самом деле не нужно знать, что такое слюна или сплиттер, так как сверху написано, что Spitter - это таблица, spitter - это еще одна таблица, которая владеет ... этим и этим ...
15

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 operation will trigger subsequent SELECT запросы для поиска существующих сущностей с этим идентификатором, в то время какpersist возможно, не нужны эти запросы.

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

6

касающиеся `Каскад & apos; и генерация идентификатора.См вопрос

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

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

1519

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

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

Merge создает новый экземпляр вашей сущности, копирует состояние из предоставленной сущности и управляет новой копией. Экземпляр, который вы передаете, не будет управляемым (любые внесенные вами изменения не будут частью транзакции - если вы не вызовете 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.

Этот ответ мог бы быть улучшен, если бы он также охватывал случаи, когда объединяемый / сохраняемый объект уже существует в постоянном контексте (или, по крайней мере, прояснял, что он описывает поведение только тогда, когда сохраненный / объединенный объект не существует)
Я хотел бы использоватьSession.persist() для отдельного объекта, но сначала мне нужно повторно связать его с сеансом. К несчастью,Session.lock(entity, LockMode.NONE) терпит неудачу за исключением того, что ленивые коллекции не инициализируются. Я делаю что-то неправильно?
А как насчет идентификаторов? Если у меня есть@GeneratedId я могу получить это в сценарии 2?
@dma_k: похоже, вы используете Hibernate. Я менее знаком с Hibernate, чем с JPA - но в JPA, если вы вызовете EntityManager.persist () и передадите отдельную сущность, вы: a) немедленно получите EntityExistsException или b) получите другое PersistenceException во время сброса / фиксации. Может быть, я неправильно понял вопрос здесь?
Майк: "Merge создает новый экземпляр ...": это не всегда так. Если EntityManager находит уже управляемый объект в своем контексте, он возвращает этот экземпляр (после обновления полей). Пожалуйста, отредактируйте свой ответ, тогда я буду голосовать за него.
1

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

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

Data needs to be persisted, but once in a while a record exists and an update is called for. In this case you could try a persist and if it throws an EntityExistsException, you look it up and combine the data:

try { entityManager.persist(entity) }

catch(EntityExistsException exception) { /* retrieve and merge */ }

Persisted data needs to be updated, but once in a while there is no record for the data yet. In this case you look it up, and do a persist if the entity is missing:

entity = entityManager.find(key);

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

else { /* merge */ }

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

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

If the changes are usually small, apply them to the managed entity. If changes are common, copy the ID from the persisted entity, as well as unaltered data. Then call EntityManager::merge() to replace the old content.
28

persist().

If X is a detached object, the EntityExistsException may be thrown when the persist operation is invoked, or the EntityExistsException or another PersistenceException may be thrown at flush or commit time.

Итак, используяpersist() подойдет, когда объектought not быть отдельным объектом. Вы можете предпочесть, чтобы код бросилPersistenceException так что быстро не получается.

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

@GeneratedValue не имеет разного значения для merge () и persist ()
+1 за & quot;merge() however must have an object with the @Id already generated.& Quot ;. Всякий раз, когда EntityManager не находит значение для поля идентификатора объекта, оно сохраняется (вставляется) в БД.
Сначала я не поняла этого, так как не понимала состояния. Надеюсь, это поможет кому-то, как и мне.docs.jboss.org/hibernate/core/3.6/reference/en-US/html/…
37

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

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

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

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

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

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

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

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

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

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

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

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

New (Transient)

A newly created object that hasn’t ever been associated with a Hibernate Session (a.k.a Persistence Context) and is not mapped to any database table row is considered to be in the New (Transient) state.

To become persisted we need to either explicitly call the EntityManager#persist method or make use of the transitive persistence mechanism.

Persistent (Managed)

A persistent entity has been associated with a database table row and it’s being managed by the current running Persistence Context. Any change made to such entity is going to be detected and propagated to the database (during the Session flush-time). With Hibernate, we no longer have to execute INSERT/UPDATE/DELETE statements. Hibernate employs a transactional write-behind working style and changes are synchronized at the very last responsible moment, during the current Session flush-time.

Detached

Once the current running Persistence Context is closed all the previously managed entities become detached. Successive changes will no longer be tracked and no automatic database synchronization is going to happen.

To associate a detached entity to an active Hibernate Session, you can choose one of the followi,ng options:

Reattaching

Hibernate (but not JPA 2.1) supports reattaching through the Session#update method. A Hibernate Session can only associate one Entity object for a given database row. This is because the Persistence Context acts as an in-memory cache (first level cache) and only one value (entity) is associated to a given key (entity type and database identifier). An entity can be reattached only if there is no other JVM object (matching the same database row) already associated to the current Hibernate Session.

Merging

The merge is going to copy the detached entity state (source) to a managed entity instance (destination). If the merging entity has no equivalent in the current Session, one will be fetched from the database. The detached object instance will continue to remain detached even after the merge operation.

Removed

Although JPA demands that managed entities only are allowed to be removed, Hibernate can also delete detached entities (but only through a Session#delete method call). A removed entity is only scheduled for deletion and the actual database DELETE statement will be executed during Session flush-time.

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

enter image description here

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

enter image description here

можешь посмотретьstackoverflow.com/questions/46214322/…?
Добро пожаловать! Если вам понравился этот ответ, держу пари, вы полюбите мойJPA and Hibernate tutorials (over 100 and counting).
Таким образом, нет возможности изменить порядок работы для orphanremoval = true?
@gstackoverflow Ответ, который вы получили, правильный. Для более подробной информации, проверьтеthis article или моя книга,High-Performance Java Persistence.
Я не могу поверить, что вы даете такие хорошие ответы, и к ним нет комментариев. Я хотел сказать спасибо!

Похожие вопросы