Pregunta JPA EntityManager: ¿Por qué usar persist () sobre merge ()?


EntityManager.merge() puede insertar nuevos objetos y actualizar los existentes.

¿Por qué querría uno usar persist() (que solo puede crear nuevos objetos)?


839
2017-07-01 16:03


origen


Respuestas:


De cualquier manera agregará una entidad a PersistenceContext, la diferencia está en lo que haga con la entidad después.

Persist toma una instancia de entidad, la agrega al contexto y la hace administrar (es decir, las futuras actualizaciones de la entidad se rastrearán).

Merge crea una nueva instancia de su entidad, copia el estado de la entidad suministrada y hace que la nueva copia sea administrada. La instancia que ingrese no se administrará (los cambios que realice no serán parte de la transacción, a menos que llame de nuevo a la combinación).

Tal vez un ejemplo de código ayude.

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)

Los escenarios 1 y 3 son aproximadamente equivalentes, pero hay algunas situaciones en las que desearía usar el escenario 2.


1455
2017-07-01 18:28



Persistir y fusionar son para dos propósitos diferentes (no son alternativas en absoluto).

(editado para expandir la información de diferencias)

persistir:

  • Insertar un nuevo registro en la base de datos
  • Adjunte el objeto al administrador de la entidad.

unir:

  • Encuentre un objeto adjunto con el mismo ID y actualícelo.
  • Si existe, actualice y devuelva el objeto ya adjunto.
  • Si no existe, inserte el nuevo registro en la base de datos.

persistir () eficiencia:

  • Podría ser más eficiente para insertar un nuevo registro en una base de datos que merge ().
  • No duplica el objeto original.

persistencia () semántica:

  • Se asegura de que está insertando y no actualizando por error.

Ejemplo:

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

De esta manera solo existe 1 objeto adjunto para cualquier registro en el administrador de entidades.

merge () para una entidad con una identificación es algo así como:

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

Aunque si está conectado a MySQL merge () podría ser tan eficiente como persist () usando una llamada a INSERT con la opción ON DUPLICATE KEY UPDATE, JPA es una programación de muy alto nivel y no puede suponer que va a ser así en todas partes.


151
2018-06-11 11:04



Si está utilizando el generador asignado, el uso de merge en lugar de persist puede causar una declaración SQL redundante, por lo tanto, afectando el rendimiento.

También, fusión de llamadas para entidades administradas también es un error ya que Hibernate administra automáticamente las entidades administradas y su estado está sincronizado con el registro de la base de datos por el mecanismo de control sucio sobre enjuagar el contexto de persistencia.

Para comprender cómo funciona todo esto, primero debe saber que Hibernate cambia la mentalidad del desarrollador de las declaraciones SQL a transiciones de estado de entidad.

Una vez que Hibernate gestiona activamente una entidad, todos los cambios se propagarán automáticamente a la base de datos.

Hibernate monitorea las entidades adjuntas actualmente. Pero para que una entidad se administre, debe estar en el estado de entidad correcto.

Primero, debemos definir todos los estados de la entidad:

  • Nuevo (Transitorio)

    Un objeto recién creado que nunca se ha asociado con un Hibernate Session (a.k.a Persistence Context) y no se asigna a ninguna fila de tabla de base de datos se considera que está en el estado Nuevo (Transitorio).

    Para persistir necesitamos llamar explícitamente al EntityManager#persist método o hacer uso del mecanismo de persistencia transitiva.

  • Persistente (Administrado)

    Una entidad persistente se ha asociado con una fila de tabla de base de datos y está siendo administrada por el contexto de persistencia en ejecución actual. Cualquier cambio realizado en dicha entidad se detectará y se propagará a la base de datos (durante el tiempo de descarga de la sesión). Con Hibernate, ya no tenemos que ejecutar instrucciones INSERT / UPDATE / DELETE. Hibernate emplea un escritura transaccional detrás el estilo de trabajo y los cambios se sincronizan en el último momento responsable, durante el actual Session tiempo de lavado.

  • Separado

    Una vez que se cierra el Contexto de Persistencia en ejecución actual, todas las entidades administradas previamente se separan. Los cambios sucesivos ya no se rastrearán y no habrá sincronización automática de la base de datos.

    Para asociar una entidad separada a una sesión de Hibernate activa, puede elegir una de las siguientes opciones:

    • Volver a unir

      Hibernate (pero no JPA 2.1) admite volver a conectar a través del método de actualización Session #. Una sesión de Hibernate solo puede asociar un objeto de entidad para una fila de base de datos dada. Esto se debe a que el Contexto de persistencia actúa como un caché en memoria (caché de primer nivel) y solo un valor (entidad) está asociado a una clave determinada (tipo de entidad e identificador de base de datos). Una entidad puede volverse a vincular solo si no hay otro objeto JVM (que coincida con la misma fila de la base de datos) ya asociado a la sesión de Hibernate actual.

    • Fusión

    La fusión va a copiar el estado de entidad separada (fuente) a una instancia de entidad gestionada (destino). Si la entidad fusionada no tiene equivalente en la sesión actual, se obtendrá uno de la base de datos. La instancia del objeto separado continuará estando desconectada incluso después de la operación de fusión.

  • Remoto

    Aunque JPA exige que solo se permita eliminar entidades gestionadas, Hibernate también puede eliminar entidades separadas (pero solo a través de una llamada al método de eliminación de sesión #). Una entidad eliminada solo está programada para borrarse y la declaración DELETE de la base de datos real se ejecutará durante el tiempo de instalación de la sesión.

Para comprender mejor las transiciones de estado JPA, puede visualizar el siguiente diagrama:

enter image description here

O si usa la API específica de Hibernate:

enter image description here


108
2018-05-11 13:00



Me di cuenta de que cuando usaba em.merge, Conseguí un SELECT declaración para cada INSERT, incluso cuando no había ningún campo que JPA generara para mí, el campo de la clave principal era un UUID que me establecí. Cambié a em.persist(myEntityObject) y obtuve solo INSERT declaraciones entonces.


37
2018-01-18 21:14



La especificación JPA dice lo siguiente sobre persist().

Si X es un objeto separado, el EntityExistsException puede ser arrojado cuando persiste   se invoca la operación, o el EntityExistsExceptionu otro PersistenceException puede arrojarse al enjuague o comprometer el tiempo.

Entonces usando persist() sería adecuado cuando el objeto no debe ser un objeto separado Puede preferir que el código arroje el PersistenceException entonces falla rápido

A pesar de que la especificación no está clara, persist() podría establecer el @GeneratedValue  @Id para un objeto. merge() sin embargo, debe tener un objeto con el @Id ya generado.


27
2018-03-11 17:23



Más detalles sobre la fusión que le ayudarán a usar la combinación sobre la persistencia:

Devolver una instancia administrada que no sea la entidad original es una parte crítica de la fusión   proceso. Si ya existe una instancia de entidad con el mismo identificador en el contexto de persistencia, el   proveedor sobrescribirá su estado con el estado de la entidad que se fusiona, pero el administrado   la versión que ya existía debe devolverse al cliente para que pueda ser utilizada. Si el proveedor no lo hizo   actualizar la instancia de Empleado en el contexto de persistencia, cualquier referencia a esa instancia se convertirá   inconsistente con el nuevo estado que se fusionó en.

Cuando se invoca merge () en una nueva entidad, se comporta de manera similar a la operación persist (). Agrega   la entidad al contexto de persistencia, pero en lugar de agregar la instancia de entidad original, crea un nuevo   copia y administra esa instancia en su lugar. La copia que se crea mediante la operación merge () se conserva   como si el método persist () fuera invocado en él.

En presencia de relaciones, la operación merge () intentará actualizar la entidad gestionada   apuntar a las versiones administradas de las entidades a las que hace referencia la entidad separada. Si la entidad tiene un   relación con un objeto que no tiene identidad persistente, el resultado de la operación de fusión es   indefinido Algunos proveedores pueden permitir que la copia administrada apunte al objeto no persistente,   mientras que otros podrían lanzar una excepción inmediatamente. La operación merge () puede ser opcional   en cascada en estos casos para evitar que se produzca una excepción. Cubriremos la cascada de la fusión ()   operación más adelante en esta sección. Si una entidad fusionada apunta a una entidad eliminada,   Se lanzará la excepción IllegalArgumentException.

Las relaciones de carga diferida son un caso especial en la operación de fusión. Si es una carga floja   relación no se desencadenó en una entidad antes de que se separara, esa relación será   ignorado cuando la entidad se fusiona. Si la relación se desencadenó mientras se administraba y luego se estableció en nulo mientras la entidad estaba separada, la versión administrada de la entidad también tendrá la relación despejada durante la fusión ".

Toda la información anterior fue tomada de "Pro JPA 2 Mastering the Java Persistence API" por Mike Keith y Merrick Schnicariol. Capítulo 6. Desprendimiento y fusión de secciones. Este libro es en realidad un segundo libro dedicado a JPA por los autores. Este nuevo libro tiene mucha información nueva, luego la anterior. Realmente recomiendo leer este libro para aquellos que estarán seriamente involucrados con JPA. Lamento haber publicado anónimamente mi primera respuesta.


16
2017-10-05 13:00



Hay algunas diferencias más entre merge y persist (Voy a enumerar de nuevo los que ya están publicados aquí):

D1. merge no hace que la entidad pasada se administre, sino que devuelve otra instancia que se administra. persist por otro lado, la entidad pasada será administrada:

//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. Si elimina una entidad y luego decide persistir con la entidad, puede hacerlo solo con persist (), porque merge arrojará un IllegalArgumentException.

D3. Si decidió cuidar sus identificaciones manualmente (por ejemplo, usando UUID), entonces una merge  la operación activará subsiguientes SELECT consultas para buscar entidades existentes con esa ID, mientras persist Puede que no necesite esas consultas.

D4. Hay casos en los que simplemente no confía en el código que llama a su código, y para asegurarse de que no se actualicen los datos, sino que se inserta, debe utilizar persist.


15
2017-12-02 14:13



Recibía excepciones de carga lenta en mi entidad porque estaba intentando acceder a una colección cargada que estaba en sesión.

Lo que haría sería solicitar por separado, recuperar la entidad de la sesión y luego tratar de acceder a una colección en mi página jsp que era problemática.

Para aliviar esto, actualicé la misma entidad en mi controlador y se la pasé a mi jsp, aunque me imagino que cuando vuelva a guardarla en sesión también será accesible. SessionScope y no tirar un LazyLoadingException, una modificación del ejemplo 2:

Lo siguiente me ha funcionado:

// 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!!

8
2017-10-20 16:13