Ciclo de vida de la entidad de hibernación

1. Información general

Cada entidad de Hibernate, naturalmente, tiene un ciclo de vida dentro del marco: se encuentra en un estado transitorio, administrado, separado o eliminado.

Comprender estos estados tanto a nivel conceptual como técnico es esencial para poder utilizar Hibernate correctamente.

Para aprender sobre varios métodos de Hibernate que tratan con entidades, eche un vistazo a uno de nuestros tutoriales anteriores.

2. Métodos auxiliares

A lo largo de este tutorial, usaremos constantemente varios métodos auxiliares:

  • HibernateLifecycleUtil.getManagedEntities (sesión): lo usaremos para obtener todas las entidades administradas de la tienda interna de una sesión
  • DirtyDataInspector.getDirtyEntities (): vamos a utilizar este método para obtener una lista de todas las entidades que se marcaron como 'sucias'
  • HibernateLifecycleUtil.queryCount (consulta): un método conveniente para realizar consultas de conteo (*) en la base de datos incrustada

Todos los métodos de ayuda anteriores se importan estáticamente para una mejor legibilidad. Puede encontrar sus implementaciones en el proyecto de GitHub vinculado al final de este artículo.

3. Se trata de un contexto de persistencia

Antes de entrar en el tema del ciclo de vida de la entidad, primero debemos comprender el contexto de persistencia .

En pocas palabras, el contexto de persistencia se encuentra entre el código del cliente y el almacén de datos . Es un área de preparación donde los datos persistentes se convierten en entidades, listos para ser leídos y alterados por el código del cliente.

Teóricamente hablando, el contexto de persistencia es una implementación del patrón de Unidad de Trabajo. Realiza un seguimiento de todos los datos cargados, realiza un seguimiento de los cambios de esos datos y es responsable de sincronizar eventualmente cualquier cambio en la base de datos al final de la transacción comercial.

JPA EntityManager y la sesión de Hibernate son una implementación del concepto de contexto de persistencia . A lo largo de este artículo, usaremos Hibernate Session para representar el contexto de persistencia.

El estado del ciclo de vida de la entidad de Hibernate explica cómo se relaciona la entidad con un contexto de persistencia , como veremos a continuación.

4. Entidad gestionada

Una entidad administrada es una representación de una fila de la tabla de la base de datos (aunque esa fila no tiene que existir todavía en la base de datos).

Esto es administrado por la sesión que se está ejecutando actualmente , y cada cambio realizado en él será rastreado y propagado a la base de datos automáticamente .

La sesión carga la entidad de la base de datos o vuelve a adjuntar una entidad separada. Analizaremos las entidades independientes en la sección 5.

Observemos un código para aclararlo.

Nuestra aplicación de muestra define una entidad, la clase FootballPlayer . Al inicio, inicializaremos el almacén de datos con algunos datos de muestra:

+-------------------+-------+ | Name | ID | +-------------------+-------+ | Cristiano Ronaldo | 1 | | Lionel Messi | 2 | | Gianluigi Buffon | 3 | +-------------------+-------+

Digamos que queremos cambiar el nombre de Buffon para empezar; queremos poner su nombre completo Gianluigi Buffon en lugar de Gigi Buffon.

Primero, necesitamos comenzar nuestra unidad de trabajo obteniendo una sesión:

Session session = sessionFactory.openSession();

En un entorno de servidor, podemos inyectar una sesión a nuestro código a través de un proxy sensible al contexto. El principio sigue siendo el mismo: necesitamos una sesión para encapsular la transacción comercial de nuestra unidad de trabajo.

A continuación, le indicaremos a nuestra sesión que cargue los datos del almacén persistente:

assertThat(getManagedEntities(session)).isEmpty(); List players = s.createQuery("from FootballPlayer").getResultList(); assertThat(getManagedEntities(session)).size().isEqualTo(3); 

La primera vez que obtenemos una sesión , su persistente tienda de contexto está vacía, como se muestra en nuestro primer afirman comunicado.

A continuación, ejecutamos una consulta que recupera datos de la base de datos, crea una representación de la entidad de los datos y finalmente devuelve la entidad para que la usemos.

Internamente, la sesión realiza un seguimiento de todas las entidades que carga en el almacén de contexto persistente. En nuestro caso, la tienda interna de la sesión contendrá 3 entidades después de la consulta.

Ahora cambiemos el nombre de Gigi:

Transaction transaction = session.getTransaction(); transaction.begin(); FootballPlayer gigiBuffon = players.stream() .filter(p -> p.getId() == 3) .findFirst() .get(); gigiBuffon.setName("Gianluigi Buffon"); transaction.commit(); assertThat(getDirtyEntities()).size().isEqualTo(1); assertThat(getDirtyEntities().get(0).getName()).isEqualTo("Gianluigi Buffon");

4.1. ¿Como funciona?

Al llamar a la transacción commit () o flush () , la sesión encontrará cualquier entidad sucia de su lista de seguimiento y sincronizará el estado con la base de datos.

Tenga en cuenta que no necesitamos llamar a ningún método para notificar a Session que cambiamos algo en nuestra entidad; dado que es una entidad administrada, todos los cambios se propagan a la base de datos automáticamente.

Una entidad gestionada es siempre una entidad persistente; debe tener un identificador de base de datos, aunque la representación de la fila de la base de datos aún no se haya creado, es decir, la declaración INSERT está pendiente del final de la unidad de trabajo.

Consulte el capítulo sobre entidades transitorias a continuación.

5. Entidad independiente

Una entidad separada es solo una entidad ordinaria POJO cuyo valor de identidad corresponde a una fila de base de datos. La diferencia con una entidad administrada es que ya no se rastrea mediante ningún contexto de persistencia .

Una entidad puede desconectarse cuando la sesión utilizada para cargarla se cerró, o cuando llamamos a Session.evict (entidad) o Session.clear () .

Veámoslo en el código:

FootballPlayer cr7 = session.get(FootballPlayer.class, 1L); assertThat(getManagedEntities(session)).size().isEqualTo(1); assertThat(getManagedEntities(session).get(0).getId()).isEqualTo(cr7.getId()); session.evict(cr7); assertThat(getManagedEntities(session)).size().isEqualTo(0);

Nuestro contexto de persistencia no rastreará los cambios en entidades separadas:

cr7.setName("CR7"); transaction.commit(); assertThat(getDirtyEntities()).isEmpty();

Session.merge (entidad) /Session.update (entidad) puede (re) adjuntar una sesión :

FootballPlayer messi = session.get(FootballPlayer.class, 2L); session.evict(messi); messi.setName("Leo Messi"); transaction.commit(); assertThat(getDirtyEntities()).isEmpty(); transaction = startTransaction(session); session.update(messi); transaction.commit(); assertThat(getDirtyEntities()).size().isEqualTo(1); assertThat(getDirtyEntities().get(0).getName()).isEqualTo("Leo Messi");

Para obtener referencias sobre Session.merge () y Session.update (), consulte aquí.

5.1. El campo de la identidad es todo lo que importa

Echemos un vistazo a la siguiente lógica:

FootballPlayer gigi = new FootballPlayer(); gigi.setId(3); gigi.setName("Gigi the Legend"); session.update(gigi);

En el ejemplo anterior, hemos instanciado una entidad de la forma habitual a través de su constructor. Hemos llenado los campos con valores y hemos establecido la identidad en 3, que corresponde a la identidad de los datos persistentes que pertenecen a Gigi Buffon. Llamar a update () tiene exactamente el mismo efecto que si hubiéramos cargado la entidad desde otro contexto de persistencia .

De hecho, Session no distingue de dónde se originó una entidad re-adjunta.

Es un escenario bastante común en aplicaciones web construir entidades separadas a partir de valores de formulario HTML.

En lo que respecta a Session , una entidad separada es solo una entidad simple cuyo valor de identidad corresponde a datos persistentes.

Tenga en cuenta que el ejemplo anterior solo tiene un propósito de demostración. y necesitamos saber exactamente lo que estamos haciendo. De lo contrario, podríamos terminar con valores nulos en nuestra entidad si solo establecemos el valor en el campo que queremos actualizar, dejando el resto intacto (por lo tanto, efectivamente nulo).

6. Entidad transitoria

Una entidad transitoria es simplemente un objeto de entidad que no tiene representación en el almacén persistente y no es administrado por ninguna sesión .

Un ejemplo típico de una entidad transitoria sería instanciar una nueva entidad a través de su constructor.

Para hacer que una entidad transitoria sea persistente , necesitamos llamar a Session.save (entidad) o Session.saveOrUpdate (entidad):

FootballPlayer neymar = new FootballPlayer(); neymar.setName("Neymar"); session.save(neymar); assertThat(getManagedEntities(session)).size().isEqualTo(1); assertThat(neymar.getId()).isNotNull(); int count = queryCount("select count(*) from Football_Player where name="Neymar""); assertThat(count).isEqualTo(0); transaction.commit(); count = queryCount("select count(*) from Football_Player where name="Neymar""); assertThat(count).isEqualTo(1);

Tan pronto como ejecutamos Session.save (entidad) , a la entidad se le asigna un valor de identidad y es administrada por la Sesión . Sin embargo, es posible que aún no esté disponible en la base de datos, ya que la operación INSERT puede retrasarse hasta el final de la unidad de trabajo.

7. Entidad eliminada

Una entidad está en un estado eliminado (eliminado) si se ha llamado a Session.delete (entidad) y la sesión ha marcado la entidad para su eliminación. El comando DELETE en sí puede emitirse al final de la unidad de trabajo.

Veámoslo en el siguiente código:

session.delete(neymar); assertThat(getManagedEntities(session).get(0).getStatus()).isEqualTo(Status.DELETED);

Sin embargo, observe que la entidad permanece en el almacén de contexto persistente hasta el final de la unidad de trabajo.

8. Conclusión

El concepto de contexto de persistencia es fundamental para comprender el ciclo de vida de las entidades de Hibernate. Hemos aclarado el ciclo de vida examinando los ejemplos de código que demuestran cada estado.

Como de costumbre, el código utilizado en este artículo se puede encontrar en GitHub.