1. Introducción
En este artículo discutiremos las diferencias entre varios métodos de la interfaz de sesión : guardar , persistir , actualizar , fusionar , guardar o actualizar .
Esta no es una introducción a Hibernate y ya debería conocer los conceptos básicos de configuración, mapeo relacional de objetos y trabajo con instancias de entidad. Para obtener un artículo introductorio a Hibernate, visite nuestro tutorial sobre Hibernate 4 con Spring.
2. Sesión como implementación de contexto de persistencia
La sesión de interfaz tiene varios métodos que finalmente se traducen en ahorro de los datos a la base de datos: persistir , guardar , actualización , fusión , saveOrUpdate . Para entender la diferencia entre estos métodos, primero debemos discutir el propósito de la sesión como un contexto de persistencia y la diferencia entre los estados de las instancias de entidad en relación con la sesión .
También debemos comprender la historia del desarrollo de Hibernate que condujo a algunos métodos API parcialmente duplicados.
2.1. Administrar instancias de entidades
Aparte del mapeo relacional de objetos en sí, uno de los problemas que Hibernate tenía la intención de resolver es el problema de administrar entidades durante el tiempo de ejecución. La noción de "contexto de persistencia" es la solución de Hibernate a este problema. El contexto de persistencia se puede considerar como un contenedor o un caché de primer nivel para todos los objetos que cargó o guardó en una base de datos durante una sesión.
La sesión es una transacción lógica, cuyos límites están definidos por la lógica empresarial de su aplicación. Cuando trabaja con la base de datos a través de un contexto de persistencia, y todas sus instancias de entidad están adjuntas a este contexto, siempre debe tener una única instancia de entidad para cada registro de base de datos con el que haya interactuado durante la sesión.
En Hibernate, el contexto de persistencia está representado por la instancia org.hibernate.Session . Para JPA, es javax.persistence.EntityManager . Cuando usamos Hibernate como proveedor de JPA y operamos a través de la interfaz EntityManager , la implementación de esta interfaz básicamente envuelve el objeto Session subyacente . Sin embargo, Hibernate Session proporciona una interfaz más rica con más posibilidades, por lo que a veces es útil trabajar con Session directamente .
2.2. Estados de instancias de entidad
Cualquier instancia de entidad en su aplicación aparece en uno de los tres estados principales en relación con el contexto de persistencia de la sesión :
- transitorio : esta instancia no está, y nunca estuvo, adjunta a una sesión ; esta instancia no tiene filas correspondientes en la base de datos; generalmente es solo un nuevo objeto que ha creado para guardar en la base de datos;
- persistente : esta instancia está asociada con un objeto de sesión único ; al vaciar la sesión en la base de datos, se garantiza que esta entidad tendrá un registro consistente correspondiente en la base de datos;
- desconectado : esta instancia se adjuntó una vez a una sesión (en un estado persistente ), pero ahora no lo está; una instancia entra en este estado si la desaloja del contexto, borra o cierra la sesión o somete la instancia a un proceso de serialización / deserialización.
A continuación, se muestra un diagrama de estado simplificado con comentarios sobre los métodos de sesión que hacen que se produzcan las transiciones de estado.

Cuando la instancia de la entidad está en el estado persistente , todos los cambios que realice en los campos mapeados de esta instancia se aplicarán a los registros y campos de la base de datos correspondientes al vaciar la sesión . La instancia persistente se puede considerar como "en línea", mientras que la instancia separada se ha "desconectado" y no se supervisa para detectar cambios.
Esto significa que cuando cambia los campos de un objeto persistente , no tiene que llamar a save , update ni a ninguno de esos métodos para obtener estos cambios en la base de datos: todo lo que necesita es confirmar la transacción o vaciar o cerrar la sesión. , cuando haya terminado.
2.3. Conformidad con la especificación JPA
Hibernate fue la implementación de ORM de Java más exitosa. No es de extrañar que la especificación de la API de persistencia de Java (JPA) haya sido fuertemente influenciada por la API de Hibernate. Desafortunadamente, también hubo muchas diferencias: algunas importantes, otras más sutiles.
Para actuar como una implementación del estándar JPA, las API de Hibernate tuvieron que ser revisadas. Se agregaron varios métodos a la interfaz de sesión para que coincida con la interfaz de EntityManager. Estos métodos tienen el mismo propósito que los métodos "originales", pero se ajustan a la especificación y, por lo tanto, tienen algunas diferencias.
3. Diferencias entre las operaciones
Es importante entender desde el principio que todos los métodos ( persisten , guardar , actualización , fusión , saveOrUpdate ) no dan como resultado inmediato en las correspondientes SQL de UPDATE o INSERT declaraciones. El guardado real de datos en la base de datos ocurre al confirmar la transacción o vaciar la sesión .
Los métodos mencionados básicamente gestionan el estado de las instancias de entidad mediante la transición entre diferentes estados a lo largo del ciclo de vida.
Como entidad de ejemplo, usaremos una entidad simple asignada por anotaciones Person :
@Entity public class Person { @Id @GeneratedValue private Long id; private String name; // ... getters and setters }
3.1. Persistir
El método de persistencia está destinado a agregar una nueva instancia de entidad al contexto de persistencia, es decir, hacer la transición de una instancia de estado transitorio a persistente .
Por lo general, lo llamamos cuando queremos agregar un registro a la base de datos (persistir una instancia de entidad):
Person person = new Person(); person.setName("John"); session.persist(person);
¿Qué sucede después de que se llama al método de persistencia ? El objeto persona ha pasado de un estado transitorio a un estado persistente . El objeto está ahora en el contexto de persistencia, pero aún no se ha guardado en la base de datos. La generación de declaraciones INSERT se producirá solo al realizar la transacción, vaciar o cerrar la sesión.
Observe que el método de persistencia tiene un tipo de retorno nulo . Opera sobre el objeto pasado "en el lugar", cambiando su estado. La variable de persona hace referencia al objeto persistente real.
Este método es una adición posterior a la interfaz de sesión. La principal característica diferenciadora de este método es que se ajusta a la especificación JSR-220 (persistencia EJB). La semántica de este método está estrictamente definida en la especificación, que básicamente establece que:
- una instancia transitoria se vuelve persistente (y la operación cae en cascada a todas sus relaciones con cascade = PERSIST o cascade = ALL ),
- si una instancia ya es persistente , entonces esta llamada no tiene ningún efecto para esta instancia en particular (pero aún cae en cascada a sus relaciones con cascade = PERSIST o cascade = ALL ),
- if an instance is detached, you should expect an exception, either upon calling this method, or upon committing or flushing the session.
Notice that there is nothing here that concerns the identifier of an instance. The spec does not state that the id will be generated right away, regardless of the id generation strategy. The specification for the persist method allows the implementation to issue statements for generating id on commit or flush, and the id is not guaranteed to be non-null after calling this method, so you should not rely upon it.
You may call this method on an already persistent instance, and nothing happens. But if you try to persist a detached instance, the implementation is bound to throw an exception. In the following example we persist the entity, evict it from the context so it becomes detached, and then try to persist again. The second call to session.persist() causes an exception, so the following code will not work:
Person person = new Person(); person.setName("John"); session.persist(person); session.evict(person); session.persist(person); // PersistenceException!
3.2. Save
The save method is an “original” Hibernate method that does not conform to the JPA specification.
Its purpose is basically the same as persist, but it has different implementation details. The documentation for this method strictly states that it persists the instance, “first assigning a generated identifier”. The method is guaranteed to return the Serializable value of this identifier.
Person person = new Person(); person.setName("John"); Long id = (Long) session.save(person);
The effect of saving an already persisted instance is the same as with persist. Difference comes when you try to save a detached instance:
Person person = new Person(); person.setName("John"); Long id1 = (Long) session.save(person); session.evict(person); Long id2 = (Long) session.save(person);
The id2 variable will differ from id1. The call of save on a detached instance creates a new persistent instance and assigns it a new identifier, which results in a duplicate record in a database upon committing or flushing.
3.3. Merge
The main intention of the merge method is to update a persistent entity instance with new field values from a detached entity instance.
For instance, suppose you have a RESTful interface with a method for retrieving an JSON-serialized object by its id to the caller and a method that receives an updated version of this object from the caller. An entity that passed through such serialization/deserialization will appear in a detached state.
After deserializing this entity instance, you need to get a persistent entity instance from a persistence context and update its fields with new values from this detached instance. So the merge method does exactly that:
- finds an entity instance by id taken from the passed object (either an existing entity instance from the persistence context is retrieved, or a new instance loaded from the database);
- copies fields from the passed object to this instance;
- returns newly updated instance.
In the following example we evict (detach) the saved entity from context, change the name field, and then merge the detached entity.
Person person = new Person(); person.setName("John"); session.save(person); session.evict(person); person.setName("Mary"); Person mergedPerson = (Person) session.merge(person);
Note that the merge method returns an object — it is the mergedPerson object that was loaded into persistence context and updated, not the person object that you passed as an argument. Those are two different objects, and the person object usually needs to be discarded (anyway, don't count on it being attached to persistence context).
As with persist method, the merge method is specified by JSR-220 to have certain semantics that you can rely upon:
- if the entity is detached, it is copied upon an existing persistent entity;
- if the entity is transient, it is copied upon a newly created persistent entity;
- this operation cascades for all relations with cascade=MERGE or cascade=ALL mapping;
- if the entity is persistent, then this method call does not have effect on it (but the cascading still takes place).
3.4. Update
As with persist and save, the update method is an “original” Hibernate method that was present long before the merge method was added. Its semantics differs in several key points:
- it acts upon passed object (its return type is void); the update method transitions the passed object from detached to persistent state;
- this method throws an exception if you pass it a transient entity.
In the following example we save the object, then evict (detach) it from the context, then change its name and call update. Notice that we don't put the result of the update operation in a separate variable, because the update takes place on the person object itself. Basically we're reattaching the existing entity instance to the persistence context — something the JPA specification does not allow us to do.
Person person = new Person(); person.setName("John"); session.save(person); session.evict(person); person.setName("Mary"); session.update(person);
Trying to call update on a transient instance will result in an exception. The following will not work:
Person person = new Person(); person.setName("John"); session.update(person); // PersistenceException!
3.5. SaveOrUpdate
This method appears only in the Hibernate API and does not have its standardized counterpart. Similar to update, it also may be used for reattaching instances.
Actually, the internal DefaultUpdateEventListener class that processes the update method is a subclass of DefaultSaveOrUpdateListener, just overriding some functionality. The main difference of saveOrUpdate method is that it does not throw exception when applied to a transient instance; instead, it makes this transient instance persistent. The following code will persist a newly created instance of Person:
Person person = new Person(); person.setName("John"); session.saveOrUpdate(person);
You may think of this method as a universal tool for making an object persistent regardless of its state wether it is transient or detached.
4. What to Use?
If you don't have any special requirements, as a rule of thumb, you should stick to the persist and merge methods, because they are standardized and guaranteed to conform to the JPA specification.
They are also portable in case you decide to switch to another persistence provider, but they may sometimes appear not so useful as the “original” Hibernate methods, save, update and saveOrUpdate.
5. Conclusion
Hemos discutido el propósito de los diferentes métodos de sesión de Hibernate en relación con la gestión de entidades persistentes en tiempo de ejecución. Hemos aprendido cómo estos métodos transfieren instancias de entidades a través de sus ciclos de vida y por qué algunos de estos métodos tienen una funcionalidad duplicada.
El código fuente del artículo está disponible en GitHub.