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

后端 未结 15 1449
说谎
说谎 2020-11-22 03:09

EntityManager.merge() can insert new objects and update existing ones.

Why would one want to use persist() (which can only create new objec

相关标签:
15条回答
  • You may have come here for advice on when to use persist and when to use merge. I think that it depends the situation: how likely is it that you need to create a new record and how hard is it to retrieve persisted data.

    Let's presume you can use a natural key/identifier.

    • 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 you don't have natural key/identifier, you'll have a harder time to figure out whether the entity exist or not, or how to look it up.

    The merges can be dealt with in two ways, too:

    1. If the changes are usually small, apply them to the managed entity.
    2. 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.
    0 讨论(0)
  • Persist and merge are for two different purposes (they aren't alternatives at all).

    (edited to expand differences information)

    persist:

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

    merge:

    • 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() efficiency:

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

    persist() semantics:

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

    Example:

    {
        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);
    }
    

    This way only exists 1 attached object for any register in the entity manager.

    merge() for an entity with an id is something like:

    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;
    }
    

    Although if connected to MySQL merge() could be as efficient as persist() using a call to INSERT with ON DUPLICATE KEY UPDATE option, JPA is a very high level programming and you can't assume this is going to be the case everywhere.

    0 讨论(0)
  • 2020-11-22 03:42

    There are some more differences between merge and persist (I will enumerate again those already posted here):

    D1. merge does not make the passed entity managed, but rather returns another instance that is managed. persist on the other side will make the passed entity managed:

    //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. If you remove an entity and then decide to persist the entity back, you may do that only with persist(), because merge will throw an IllegalArgumentException.

    D3. If you decided to take care manually of your IDs (e.g by using UUIDs), then a merge operation will trigger subsequent SELECT queries in order to look for existent entities with that ID, while persist may not need those queries.

    D4. There are cases when you simply do not trust the code that calls your code, and in order to make sure that no data is updated, but rather is inserted, you must use persist.

    0 讨论(0)
提交回复
热议问题