Ciclo de Vida das Entidades JPA: Entenda os 4 Estados
No JPA/Hibernate, uma entidade não é apenas um objeto Java comum. Ela passa por diferentes estados dentro do Persistence Context. Entender esses estados é a chave para evitar bugs bizarros onde dados não são salvos ou exceções inesperadas acontecem.
Onde está o seu objeto?
Imagine o Persistence Context (EntityManager) como um “vigia”. Ele decide quem ele está monitorando e quem ele ignora.
Os 4 Estados Fundamentais
- Transient (Transitório): O objeto foi acabado de ser criado com
new. Ele ainda não tem ID e o Hibernate não sabe que ele existe. Se você mudar um valor aqui, nada acontece no banco.1
User user = new User(); // Transient
- Persistent / Managed (Gerenciado): O objeto está associado a uma sessão ativa e tem um ID no banco. Qualquer alteração feita nele será detectada pelo Dirty Checking e salva automaticamente no banco no final da transação.
1 2
User user = em.find(User.class, 1L); // Managed user.setName("Novo Nome"); // Será salvo no commit!
- Detached (Desconectado): O objeto já foi gerenciado, mas a sessão foi fechada ou ele foi removido explicitamente (
em.detach()). O objeto ainda tem um ID, mas o Hibernate não o vigia mais. Alterações aqui não vão para o banco automaticamente.1
em.close(); // user agora é Detached
- Removed (Removido): O objeto está marcado para ser deletado do banco de dados no final da transação.
1
em.remove(user); // Removed
O Truque do merge()
Se você tem um objeto Detached e quer que ele volte a ser Managed, você usa o merge(). Mas atenção: o merge não altera o seu objeto; ele cria uma nova cópia gerenciada com os dados que você passou.
Por que isso importa?
Muitos erros de “entidade não salva” acontecem porque o desenvolvedor está alterando um objeto que já está no estado Detached. Outro erro comum é carregar milhares de objetos como Managed desnecessariamente, consumindo muita memória do “vigia” (Persistence Context).
Checklist de Estados da Entidade
Sempre que uma entidade não se comportar como esperado, valide estes pontos:
- O objeto foi criado com
new? (Estado: Transient - chamepersistousave). - O método está dentro de um
@Transactionalativo? (Estado: Managed - o Dirty Checking funcionará). - A sessão foi fechada ou o objeto veio de um cache externo? (Estado: Detached - use
merge). - Você chamou
repository.delete()? (Estado: Removed - o objeto ainda existe na memória até o flush). - O ID da entidade já está preenchido antes de salvar? (Pode causar um
mergeinesperado em vez depersist).