Differencebetween Persist() and Merge() in JPA and Hibernate

What is the difference between persist() and merge() in JPA and Hibernate?

JPA specification contains a very precise description of semantics of these operations, better than in javadoc:

The semantics of the persist
operation, applied to an entity X are
as follows:

  • If X is a new entity, it
    becomes managed. The entity X will be
    entered into the database at or before
    transaction commit or as a result of
    the flush operation.

  • If X is a
    preexisting managed entity, it is
    ignored by the persist operation.
    However, the persist operation is
    cascaded to entities referenced by X,
    if the relationships from X to these
    other entities are annotated with the
    cascade=PERSIST or cascade=ALL
    annotation element value or specified
    with the equivalent XML descriptor
    element.

  • If X is a removed entity,
    it becomes managed.

  • 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.

  • For
    all entities Y referenced by a
    relationship from X, if the
    relationship to Y has been annotated
    with the cascade element value
    cascade=PERSIST or cascade=ALL, the
    persist operation is applied to Y.


The semantics of the merge operation
applied to an entity X are as follows:

  • If X is a detached entity, the state
    of X is copied onto a pre-existing
    managed entity instance X' of the same
    identity or a new managed copy X' of X
    is created.

  • If X is a new entity
    instance, a new managed entity
    instance X' is created and the state
    of X is copied into the new managed
    entity instance X'.

  • If X is a
    removed entity instance, an
    IllegalArgumentException will be
    thrown by the merge operation (or the
    transaction commit will fail).

  • If X
    is a managed entity, it is ignored by
    the merge operation, however, the
    merge operation is cascaded to
    entities referenced by relationships
    from X if these relationships have
    been annotated with the cascade
    element value cascade=MERGE or
    cascade=ALL annotation.

  • For all
    entities Y referenced by relationships
    from X having the cascade element
    value cascade=MERGE or cascade=ALL, Y
    is merged recursively as Y'. For all
    such Y referenced by X, X' is set to
    reference Y'. (Note that if X is
    managed then X is the same object as
    X'.)

  • If X is an entity merged to X',
    with a reference to another entity Y,
    where cascade=MERGE or cascade=ALL is
    not specified, then navigation of the
    same association from X' yields a
    reference to a managed object Y' with
    the same persistent identity as Y.

JPA EntityManager: Why use persist() over merge()?

Either way will add an entity to a PersistenceContext, the difference is in what you do with the entity afterwards.

Persist takes an entity instance, adds it to the context and makes that instance managed (i.e. future updates to the entity will be tracked).

Merge returns the managed instance that the state was merged with. It does return something that exists in PersistenceContext or creates a new instance of your entity. In any case, it will copy the state from the supplied entity, and return a managed copy. The instance you pass in will not be managed (any changes you make will not be part of the transaction - unless you call merge again). Though you can use the returned instance (managed one).

Maybe a code example will help.

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)

Scenario 1 and 3 are roughly equivalent, but there are some situations where you'd want to use Scenario 2.

Hibernate merge vs. persist. Were there changes?

Possible Duplicate of Update Vs Merge

Whats happening here is:
Edit Mode :

List<UserRole> roles = entity.getRoles(); //Gets Existing Roles from DB
for(UserRole r : roles) {
Em.get().remove(r); //Removes Roles to existing user
}
roles.clear(); // Clean up local memory

for(RoleEnum r : selectedRoles) { // User Input Roles
UserRole role = new UserRole(entity, r); // New Entity with existing user
Em.get().persist(role); // Role Entity Referenced to existing user object, saved
}

Em.get().merge(entity); // ?? No Need in edit unless roles are stored in user table
Em.get().flush();

New User Mode :

List<UserRole> roles = entity.getRoles(); // New Detached User Entity Roles
for(UserRole r : roles) { // Probably Empty Roles Array
Em.get().remove(r); // Removed roles
}
roles.clear(); // Clean up Memory

for(RoleEnum r : selectedRoles) { // Copy from App Roles
UserRole role = new UserRole(entity, r); //Create new role
Em.get().persist(role); //Save Role to DB
}

Em.get().merge(entity); // Trying to merge non existing Entity <-- This is where error appears
Em.get().flush();

The persist method works because it has decides when to use insert or update command. Since new user entity has no ID set to it, it has no idea what to do with it, while it may have worked in past, actual behavior of mergig is very well explain in this thread merging a detached or new entity with an existing entity in hibernate/jpa best practice question

See for yourself :

If your entity is a detached entity the only thing u really need to do
is to invoke entityManager.merge(user). You dont need to exec any
finder method. If your entity is not detached but rather new (it does
not have id specified) you should find appropriate entity in the
database prior performing any modification operations on that entity
and merge it afterwards.

Another detailed reference is given here : persist() and merge() in JPA and Hibernate

Here is the reference from docs :

Serializable
save(Object object) throws HibernateException

 Persist the given transient instance, first assigning a generated identifier. (Or using the current value of the identifier property if the assigned generator is used.) This operation cascades to associated instances if the association is mapped with cascade="save-update". 
Parameters:
object - a transient instance of a persistent class
Returns:
the generated identifier
Throws:
HibernateException

persist

void persist(String entityName,
Object object)
throws HibernateException

Make a transient instance persistent. This operation cascades to associated instances if the association is mapped with cascade="persist".

The semantics of this method are defined by JSR-220.

Parameters:
object - a transient instance to be made persistent
Throws:
HibernateException

merge

Object merge(String entityName,
Object object)
throws HibernateException

Copy the state of the given object onto the persistent object with the same identifier. If there is no persistent instance currently associated with the session, it will be loaded. Return the persistent instance. If the given instance is unsaved, save a copy of and return it as a newly persistent instance. The given instance does not become associated with the session. This operation cascades to associated instances if the association is mapped with cascade="merge".

The semantics of this method are defined by JSR-220.

Parameters:
object - a detached instance with state to be copied
Returns:
an updated persistent instance
Throws:
HibernateException

save() and persist() result in an SQL INSERT, delete() in an SQL
DELETE and update() or merge() in an SQL UPDATE. Changes to persistent
instances are detected at flush time and also result in an SQL UPDATE.
saveOrUpdate() and replicate() result in either an INSERT or an
UPDATE.

Conclusion: Functions are behaving as they are intended.

JPA merge vs. persist

It's not a good idea using merge when a persist suffices - merge does quite a lot more of work. The topic has been discussed on StackOverflow before, and this article explains in detail the differences, with some nice flow diagrams to make things clear.

What are the differences between the different saving methods in Hibernate?

Here's my understanding of the methods. Mainly these are based on the API though as I don't use all of these in practice.

saveOrUpdate
Calls either save or update depending on some checks. E.g. if no identifier exists, save is called. Otherwise update is called.

save
Persists an entity. Will assign an identifier if one doesn't exist. If one does, it's essentially doing an update. Returns the generated ID of the entity.

update
Attempts to persist the entity using an existing identifier. If no identifier exists, I believe an exception is thrown.

saveOrUpdateCopy
This is deprecated and should no longer be used. Instead there is...

merge
Now this is where my knowledge starts to falter. The important thing here is the difference between transient, detached and persistent entities. For more info on the object states, take a look here. With save & update, you are dealing with persistent objects. They are linked to a Session so Hibernate knows what has changed. But when you have a transient object, there is no session involved. In these cases you need to use merge for updates and persist for saving.

persist
As mentioned above, this is used on transient objects. It does not return the generated ID.

JPA OneToOne difference between cascade merge and persist

I would not dream to challenge the uber answer of DannyMo, but I wanted to make an addition:

Persist and merge are designed as a way to keep one managed instance of certain object.

If you use persist it means the object does not exist yet, so making it a unique managed instance doesn't hurt.

When you use merge, you take into consideration that a managed instance of the object may already exist. You don't wanna replace that unique managed instance, because some other object might reference it, believing it is the managed object.

If you want to make operations on the object after the merge, the correct merge would look like this:

managedObject = em.merge(object); // or just object = em.merge(object)
//You cannot do it with persist since it returns null

If you tried to check if managedObject and object points to the same object instance checking if(managedObject == object) you will get false (true is possible when you use merge on already managed object and the operation is ignored).

If you use merge on outdated version of object, which you passed as argument to the previous merge, jpa doesn't know how to find the right object since it has yet no id. It is assumed that it is a new object and new managed instance will be created.

I'm quite a rookie. Please correct me if I am wrong anywhere.



Related Topics



Leave a reply



Submit