Como expliqué en este artículo , debe preferir los métodos JPA la mayor parte del tiempo y las updatetareas de procesamiento por lotes.
Una entidad JPA o Hibernate puede estar en uno de los siguientes cuatro estados:
- Transitoria (nueva)
- Gestionado (persistente)
- Separado
- Eliminado (eliminado)
La transición de un estado a otro se realiza a través de los métodos EntityManager o Session.
Por ejemplo, el JPA EntityManagerproporciona los siguientes métodos de transición de estado de entidad.

Hibernate Sessionimplementa todos los EntityManagermétodos JPA y proporciona algunos métodos adicionales de transición de estado de entidad como save, saveOrUpdatey update.

Persistir
Para cambiar el estado de una entidad de Transitoria (Nueva) a Administrada (Persistente), podemos usar el persistmétodo ofrecido por el JPA EntityManagerque también es heredado por el Hibernate Session.
El persistmétodo activa un PersistEventque es manejado por el DefaultPersistEventListeneroyente de eventos Hibernate.
Por lo tanto, al ejecutar el siguiente caso de prueba:
doInJPA(entityManager -> {
Book book = new Book()
.setIsbn("978-9730228236")
.setTitle("High-Performance Java Persistence")
.setAuthor("Vlad Mihalcea");
entityManager.persist(book);
LOGGER.info(
"Persisting the Book entity with the id: {}",
book.getId()
);
});
Hibernate genera las siguientes instrucciones SQL:
CALL NEXT VALUE FOR hibernate_sequence
-- Persisting the Book entity with the id: 1
INSERT INTO book (
author,
isbn,
title,
id
)
VALUES (
'Vlad Mihalcea',
'978-9730228236',
'High-Performance Java Persistence',
1
)
Observe que idse asigna antes de adjuntar la Bookentidad al contexto de persistencia actual. Esto es necesario porque las entidades administradas se almacenan en una Mapestructura donde la clave está formada por el tipo de entidad y su identificador y el valor es la referencia de la entidad. Esta es la razón por la cual el JPA EntityManagery el Hibernate Sessionse conocen como el caché de primer nivel.
Al llamar persist, la entidad solo se adjunta al contexto de persistencia actualmente en ejecución, y el INSERT puede posponerse hasta que flushse llame.
La única excepción es el generador de IDENTIDAD que activa el INSERTAR de inmediato, ya que esa es la única forma en que puede obtener el identificador de la entidad. Por esta razón, Hibernate no puede insertar por lotes para entidades que usan el generador IDENTITY. Para obtener más detalles sobre este tema, consulte este artículo .
Salvar
El savemétodo específico de Hibernate es anterior a JPA y ha estado disponible desde el comienzo del proyecto Hibernate.
El savemétodo activa un SaveOrUpdateEventque es manejado por el DefaultSaveOrUpdateEventListeneroyente de eventos Hibernate. Por lo tanto, el savemétodo es equivalente a los métodos updatey saveOrUpdate.
Para ver cómo funciona el savemétodo, considere el siguiente caso de prueba:
doInJPA(entityManager -> {
Book book = new Book()
.setIsbn("978-9730228236")
.setTitle("High-Performance Java Persistence")
.setAuthor("Vlad Mihalcea");
Session session = entityManager.unwrap(Session.class);
Long id = (Long) session.save(book);
LOGGER.info(
"Saving the Book entity with the id: {}",
id
);
});
Al ejecutar el caso de prueba anterior, Hibernate genera las siguientes instrucciones SQL:
CALL NEXT VALUE FOR hibernate_sequence
-- Saving the Book entity with the id: 1
INSERT INTO book (
author,
isbn,
title,
id
)
VALUES (
'Vlad Mihalcea',
'978-9730228236',
'High-Performance Java Persistence',
1
)
Como puede ver, el resultado es idéntico a la persistllamada al método. Sin embargo, a diferencia persist, el savemétodo devuelve el identificador de entidad.
Para más detalles, mira este artículo .
Actualizar
El updatemétodo específico de Hibernate está destinado a evitar el mecanismo de verificación sucio y forzar una actualización de la entidad en el momento del vaciado.
El updatemétodo activa un SaveOrUpdateEventque es manejado por el DefaultSaveOrUpdateEventListeneroyente de eventos Hibernate. Por lo tanto, el updatemétodo es equivalente a los métodos savey saveOrUpdate.
Para ver cómo funciona el updatemétodo, considere el siguiente ejemplo que persiste una Bookentidad en una transacción, luego la modifica mientras la entidad está en el estado desconectado, y fuerza la ACTUALIZACIÓN de SQL utilizando la updatellamada al método.
Book _book = doInJPA(entityManager -> {
Book book = new Book()
.setIsbn("978-9730228236")
.setTitle("High-Performance Java Persistence")
.setAuthor("Vlad Mihalcea");
entityManager.persist(book);
return book;
});
LOGGER.info("Modifying the Book entity");
_book.setTitle(
"High-Performance Java Persistence, 2nd edition"
);
doInJPA(entityManager -> {
Session session = entityManager.unwrap(Session.class);
session.update(_book);
LOGGER.info("Updating the Book entity");
});
Al ejecutar el caso de prueba anterior, Hibernate genera las siguientes instrucciones SQL:
CALL NEXT VALUE FOR hibernate_sequence
INSERT INTO book (
author,
isbn,
title,
id
)
VALUES (
'Vlad Mihalcea',
'978-9730228236',
'High-Performance Java Persistence',
1
)
-- Modifying the Book entity
-- Updating the Book entity
UPDATE
book
SET
author = 'Vlad Mihalcea',
isbn = '978-9730228236',
title = 'High-Performance Java Persistence, 2nd edition'
WHERE
id = 1
Observe que UPDATEse ejecuta durante el vaciado del contexto de persistencia, justo antes de la confirmación, y es por eso que el Updating the Book entitymensaje se registra primero.
Utilizando @SelectBeforeUpdatepara evitar actualizaciones innecesarias
Ahora, la ACTUALIZACIÓN siempre se ejecutará, incluso si la entidad no se cambió mientras estaba en el estado desconectado. Para evitar esto, puede usar la @SelectBeforeUpdateanotación Hibernate que activará una SELECTdeclaración que se obtiene y loaded stateque luego utiliza el mecanismo de verificación sucio.
Entonces, si anotamos la Bookentidad con la @SelectBeforeUpdateanotación:
@Entity(name = "Book")
@Table(name = "book")
@SelectBeforeUpdate
public class Book {
//Code omitted for brevity
}
Y ejecute el siguiente caso de prueba:
Book _book = doInJPA(entityManager -> {
Book book = new Book()
.setIsbn("978-9730228236")
.setTitle("High-Performance Java Persistence")
.setAuthor("Vlad Mihalcea");
entityManager.persist(book);
return book;
});
doInJPA(entityManager -> {
Session session = entityManager.unwrap(Session.class);
session.update(_book);
});
Hibernate ejecuta las siguientes instrucciones SQL:
INSERT INTO book (
author,
isbn,
title,
id
)
VALUES (
'Vlad Mihalcea',
'978-9730228236',
'High-Performance Java Persistence',
1
)
SELECT
b.id,
b.author AS author2_0_,
b.isbn AS isbn3_0_,
b.title AS title4_0_
FROM
book b
WHERE
b.id = 1
Tenga en cuenta que, esta vez, no se UPDATEejecuta desde que el mecanismo de comprobación sucia de Hibernate ha detectado que la entidad no se modificó.
SaveOrUpdate
El saveOrUpdatemétodo específico de Hibernate es solo un alias para savey update.
El saveOrUpdatemétodo activa un SaveOrUpdateEventque es manejado por el DefaultSaveOrUpdateEventListeneroyente de eventos Hibernate. Por lo tanto, el updatemétodo es equivalente a los métodos savey saveOrUpdate.
Ahora, puede usar saveOrUpdatecuando desea persistir una entidad o forzar una UPDATEcomo se ilustra en el siguiente ejemplo.
Book _book = doInJPA(entityManager -> {
Book book = new Book()
.setIsbn("978-9730228236")
.setTitle("High-Performance Java Persistence")
.setAuthor("Vlad Mihalcea");
Session session = entityManager.unwrap(Session.class);
session.saveOrUpdate(book);
return book;
});
_book.setTitle("High-Performance Java Persistence, 2nd edition");
doInJPA(entityManager -> {
Session session = entityManager.unwrap(Session.class);
session.saveOrUpdate(_book);
});
Cuidado con el NonUniqueObjectException
Un problema que puede ocurrir con save, updatey saveOrUpdatees si el Contexto de persistencia ya contiene una referencia de entidad con la misma identificación y del mismo tipo que en el siguiente ejemplo:
Book _book = doInJPA(entityManager -> {
Book book = new Book()
.setIsbn("978-9730228236")
.setTitle("High-Performance Java Persistence")
.setAuthor("Vlad Mihalcea");
Session session = entityManager.unwrap(Session.class);
session.saveOrUpdate(book);
return book;
});
_book.setTitle(
"High-Performance Java Persistence, 2nd edition"
);
try {
doInJPA(entityManager -> {
Book book = entityManager.find(
Book.class,
_book.getId()
);
Session session = entityManager.unwrap(Session.class);
session.saveOrUpdate(_book);
});
} catch (NonUniqueObjectException e) {
LOGGER.error(
"The Persistence Context cannot hold " +
"two representations of the same entity",
e
);
}
Ahora, al ejecutar el caso de prueba anterior, Hibernate va a lanzar un NonUniqueObjectExceptionporque el segundoEntityManager ya contiene una Bookentidad con el mismo identificador al que pasamos update, y el Contexto de persistencia no puede contener dos representaciones de la misma entidad.
org.hibernate.NonUniqueObjectException:
A different object with the same identifier value was already associated with the session : [com.vladmihalcea.book.hpjp.hibernate.pc.Book#1]
at org.hibernate.engine.internal.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.java:651)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:284)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:227)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:92)
at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:73)
at org.hibernate.internal.SessionImpl.fireSaveOrUpdate(SessionImpl.java:682)
at org.hibernate.internal.SessionImpl.saveOrUpdate(SessionImpl.java:674)
Unir
Para evitarlo NonUniqueObjectException, debe usar elmerge método ofrecido por JPA EntityManagery heredado por Hibernate Sessiontambién.
Como se explica en este artículo , elmerge obtiene una nueva instantánea de entidad de la base de datos si no se encuentra ninguna referencia de entidad en el Contexto de persistencia, y copia el estado de la entidad separada que se pasó al mergemétodo.
El mergemétodo desencadena unMergeEventque es manejado por el DefaultMergeEventListeneroyente de eventos Hibernate.
Para ver cómo funciona el mergemétodo, considere el siguiente ejemplo que persiste a una Bookentidad en una transacción, luego la modifica mientras la entidad está en el estado separado y pasa la entidad separada amerge en un contexto de persistencia posterior.
Book _book = doInJPA(entityManager -> {
Book book = new Book()
.setIsbn("978-9730228236")
.setTitle("High-Performance Java Persistence")
.setAuthor("Vlad Mihalcea");
entityManager.persist(book);
return book;
});
LOGGER.info("Modifying the Book entity");
_book.setTitle(
"High-Performance Java Persistence, 2nd edition"
);
doInJPA(entityManager -> {
Book book = entityManager.merge(_book);
LOGGER.info("Merging the Book entity");
assertFalse(book == _book);
});
Al ejecutar el caso de prueba anterior, Hibernate ejecutó las siguientes instrucciones SQL:
INSERT INTO book (
author,
isbn,
title,
id
)
VALUES (
'Vlad Mihalcea',
'978-9730228236',
'High-Performance Java Persistence',
1
)
-- Modifying the Book entity
SELECT
b.id,
b.author AS author2_0_,
b.isbn AS isbn3_0_,
b.title AS title4_0_
FROM
book b
WHERE
b.id = 1
-- Merging the Book entity
UPDATE
book
SET
author = 'Vlad Mihalcea',
isbn = '978-9730228236',
title = 'High-Performance Java Persistence, 2nd edition'
WHERE
id = 1
Observe que la referencia de entidad devuelta por merge es diferente de la separada que pasamos al mergemétodo.
Ahora, aunque debería preferir usar JPA merge al copiar el estado de la entidad separada, el extra SELECTpuede ser problemático al ejecutar una tarea de procesamiento por lotes.
Por esta razón, debería preferir usar update cuando esté seguro de que no hay una referencia de entidad ya asociada al Contexto de persistencia actualmente en ejecución y que la entidad separada se ha modificado.
Para obtener más detalles sobre este tema, consulte este artículo. .
Conclusión
Para persistir una entidad, debe usar el persistmétodo JPA . Para copiar el estado de la entidad separada, se mergedebe preferir. El updatemétodo es útil solo para tareas de procesamiento por lotes. Los savey saveOrUpdateson solo alias updatey probablemente no debería usarlos en absoluto.
Algunos desarrolladores llaman save incluso cuando la entidad ya está administrada, pero esto es un error y desencadena un evento redundante ya que, para las entidades administradas, la ACTUALIZACIÓN se maneja automáticamente en el momento de descarga del contexto de Persistencia.
Para más detalles, mira este artículo .