Ciclo de vida da entidade do Hibernate
1. Visão geral
Cada entidade Hibernate naturalmente tem um ciclo de vida dentro da estrutura - seja em um estado transitório, gerenciado, desconectado ou excluído.
Entender esses estados tanto no nível conceitual quanto técnico é essencial para poder usar o Hibernate corretamente.
Para aprender sobre os vários métodos do Hibernate que lidam com entidades, dê uma olhada emone of our previous tutorials.
2. Métodos auxiliares
Ao longo deste tutorial, usaremos consistentemente vários métodos auxiliares:
-
HibernateLifecycleUtil.getManagedEntities(session) – vamos usá-lo para obter todas as entidades gerenciadas de um armazenamento internoSession’s
-
DirtyDataInspector.getDirtyEntities() – vamos usar este método para obter uma lista de todas as entidades que foram marcadas como 'sujas'
-
HibernateLifecycleUtil.queryCount(query) – um método conveniente para fazercount(*) consulta no banco de dados embutido
Todos os métodos auxiliares acima são importados estaticamente para melhor legibilidade. Você pode encontrar suas implementações no projeto GitHub vinculado no final deste artigo.
3. É tudo sobre o contexto de persistência
Antes de entrar no tópico do ciclo de vida da entidade, primeiro, precisamos entenderthe persistence context.
Simply put, the persistence context sits between client code and data store. É uma área de teste onde os dados persistentes são convertidos em entidades, prontos para serem lidos e alterados pelo código do cliente.
Teoricamente falando, opersistence context é uma implementação do padrãoUnit of Work. Ele controla todos os dados carregados, rastreia as alterações desses dados e é responsável por sincronizar as alterações no banco de dados no final da transação comercial.
JPAEntityManagereSession do Hibernate são uma implementação do conceitopersistence context . Ao longo deste artigo, usaremos HibernateSession para representarpersistence context.
O estado do ciclo de vida da entidade Hibernate explica como a entidade está relacionada apersistence context, como veremos a seguir.
4. Entidade Gerenciada
A managed entity is a representation of a database table row (embora essa linha não precise existir no banco de dados ainda).
Isso é gerenciado porSession atualmente em execução eevery change made on it will be tracked and propagated to the database automatically.
OSession carrega a entidade do banco de dados ou anexa novamente uma entidade separada. Discutiremos entidades desanexadas na seção 5.
Vamos observar alguns códigos para obter esclarecimentos.
Nosso aplicativo de exemplo define uma entidade, a classeFootballPlayer. Na inicialização, inicializaremos o armazenamento de dados com alguns dados de amostra:
+-------------------+-------+
| Name | ID |
+-------------------+-------+
| Cristiano Ronaldo | 1 |
| Lionel Messi | 2 |
| Gianluigi Buffon | 3 |
+-------------------+-------+
Digamos que queremos mudar o nome de Buffon para começar - queremos colocar seu nome completoGianluigi Buffon em vez de Gigi Buffon.
Primeiro, precisamos iniciar nossa unidade de trabalho obtendo umSession:
Session session = sessionFactory.openSession();
Em um ambiente de servidor, podemos injetarSession em nosso código por meio de um proxy ciente de contexto. O princípio permanece o mesmo: precisamos de umSession para encapsular a transação comercial de nossa unidade de trabalho.
A seguir, instruiremos nossoSession para carregar os dados do armazenamento persistente:
assertThat(getManagedEntities(session)).isEmpty();
List players = s.createQuery("from FootballPlayer").getResultList();
assertThat(getManagedEntities(session)).size().isEqualTo(3);
Quando obtemos pela primeira vez umSession, seu armazenamento de contexto persistente está vazio, conforme mostrado por nossa primeira instruçãoassert.
Em seguida, estamos executando uma consulta que recupera dados do banco de dados, cria uma representação de entidade dos dados e, finalmente, retorna a entidade para usarmos.
Internamente, oSession mantém o controle de todas as entidades que carrega no armazenamento de contexto persistente. Em nosso caso, o armazenamento sinternalSession’s conterá 3 entidades após a consulta.
Agora vamos mudar o nome 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?
Na chamada para a transaçãocommit() ouflush(), oSession encontrará quaisquer entidadesdirty de sua lista de rastreamento e sincronizará o estado com o banco de dados.
Observe que não precisamos chamar nenhum método para notificarSession que alteramos algo em nossa entidade - por ser uma entidade gerenciada, todas as alterações são propagadas para o banco de dados automaticamente.
Uma entidade gerenciada é sempre uma entidade persistente - ela deve ter um identificador de banco de dados, mesmo que a representação de linha do banco de dados ainda não tenha sido criada, ou seja, a instrução INSERT está pendente no final da unidade de trabalho.
Veja o capítulo sobre entidades transitórias abaixo.
5. Entidade Desanexada
A detached entity is just an ordinary entity POJO cujo valor de identidade corresponde a uma linha do banco de dados. A diferença de uma entidade gerenciada é que ela énot tracked anymore by any persistence context.
Uma entidade pode ser desconectada quando oSession usado para carregá-la foi fechado ou quando chamamosSession.evict(entity) ouSession.clear().
Vamos ver no 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);
Nosso contexto de persistência não acompanhará as alterações nas entidades desanexadas:
cr7.setName("CR7");
transaction.commit();
assertThat(getDirtyEntities()).isEmpty();
Session.merge(entity)/Session.update(entity) can (re) anexar uma sessão:
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 referência emSession.merge() eSession.update(), consultehere.
5.1. O campo da identidade é tudo que importa
Vamos dar uma olhada na seguinte lógica:
FootballPlayer gigi = new FootballPlayer();
gigi.setId(3);
gigi.setName("Gigi the Legend");
session.update(gigi);
No exemplo acima, instanciamos uma entidade da maneira usual por meio de seu construtor. Preenchemos os campos com valores e definimos a identidade como 3, que corresponde à identidade dos dados persistentes que pertencem a Gigi Buffon. Chamarupdate() tem exatamente o mesmo efeito como se carregássemos a entidade de outropersistence context.
Na verdade,Session não distingue de onde uma entidade reconectada se originou.
É um cenário bastante comum em aplicativos da web construir entidades separadas de valores de formulário HTML.
No que diz respeito aSession, uma entidade separada é apenas uma entidade simples cujo valor de identidade corresponde aos dados persistentes.
Esteja ciente de que o exemplo acima serve apenas para fins de demonstração. e precisamos saber exatamente o que estamos fazendo. Caso contrário, poderíamos acabar com valores nulos em nossa entidade se apenas definirmos o valor no campo que queremos atualizar, deixando o restante intocado (portanto, efetivamente nulo).
6. Entidade Transiente
A transient entity is simply an entity object that has no representation in the persistent storee não é gerenciado por nenhumSession.
Um exemplo típico de uma entidade transitória seria instanciar uma nova entidade por meio de seu construtor.
Para fazer uma entidade transitóriapersistent, precisamos chamarSession.save(entity) orSession.saveOrUpdate(entity):
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);
Assim que executamosSession.save(entity), a entidade recebe um valor de identidade e passa a ser gerenciada porSession. No entanto, ainda pode não estar disponível no banco de dados, pois a operação INSERT pode demorar até o final da unidade de trabalho.
7. Entidade Excluída
An entity is in a deleted (removed) state if Session.delete(entity) foi chamado eSession marcou a entidade para exclusão. O próprio comando DELETE pode ser emitido no final da unidade de trabalho.
Vamos ver no seguinte código:
session.delete(neymar);
assertThat(getManagedEntities(session).get(0).getStatus()).isEqualTo(Status.DELETED);
No entanto, observe que a entidade permanece no armazenamento de contexto persistente até o final da unidade de trabalho.
8. Conclusão
O conceito depersistence context é central para entender o ciclo de vida das entidades do Hibernate. Esclarecemos o ciclo de vida examinando os exemplos de código que demonstram cada status.
Como de costume, o código usado neste artigo pode ser encontradoover on GitHub.