Entidades Managed, Transient e Detached no Hibernate e JPA
Por Paulo Silveira em 23/11/06Esse blog tem recebido uma média alta de comentários (agradecemos!), em alguns posts temos mais de dez pessoas diferentes dando sugestões. O patinho feio é meu longo post sobre as exceptions que mais acontecem quando desenvolvemos com Hibernate e JPA, talvez por alguns casos não serem tão comuns assim, e porque JPA ainda está recente.
Apesar de terem reclamado que isto está parecendo um livro de Hibernate, vou escrever aqui a introdução mais curta da história da JPA, baseado na palestra que dei sobre esse assunto no ConexãoJava e com a ajuda do Fábio Kung e do Nico Steppat para deixá-la minimal.
Um objeto é dito transiente quando não tem representação no banco de dados e nem o EntityManager o conhece. Exemplo:
Cliente c = new Cliente();
Aqui, qualquer mudança no objeto referido por c não gerará nenhum tipo de insert ou update no banco de dados.
O oposto é quando o objeto existe no banco de dados e o EntityManager em questão possui uma referência para ele, essa entidade está managed, gerenciada pelo EntityManager. Considere em uma referência a um EntityManager no seguinte exemplo:
Cliente c = new Cliente(); // transiente
em.persist(c); // gerenciado
Ou ainda:
Cliente c = em.find(Cliente.class, 1); // gerenciado
Quando uma entidade está managed, qualquer mudança em seu estado (como uma chamada de setter) resultará em uma atualização no banco de dados no momento do commit.
O último caso é quando a entidade representa algo que possivelmente está no banco de dados, mas o EntityManager o desconhece: a entidade está fora do contexto, detached. Exemplo:
Cliente c = new Cliente();
c.setId(1);
Uma entidade também está detached quando o EntityManager de onde tiramos esse Cliente (por exemplo, quando fizemos um find ou vindo de uma Query) já não está mais aberta. Qualquer mudança nessa referência obviamente não surtirá efeito no banco de dados. Para que essa mudança faça efeito, isto é, para reattach o entidade, antes precisamos amarrá-la ao contexto de persistência. Repare que no EntityManager já pode existir uma entidade Cliente com esse mesmo id, imagine então o que aconteceria se tivéssemos um método que se chamasse reattach ou update?
Por isso o método é o merge. Ele junta a possível entidade com mesmo id que se encontra no EntityManager com a passada como argumento, e devolve a que está managed. O método merge não faz reattach. Então:
Cliente c = new Cliente();
c.setId(1);
em.merge(c);
c.setNome("Cliente com nome alterado");
Não surtirá efeito! Aqui você precisava antes ter pego o que o merge devolveu. Repare na pequena alteração:
Cliente c = new Cliente();
c.setId(1);
c = em.merge(c);
c.setNome("Cliente com nome alterado");
Pronto. Uma pequena introdução sobre o ciclo de vida de uma entidade em relação a um EntityManager: transient (a especificação chama de new), managed e detached! Ainda temos o estado removed, quando uma entidade está marcada para a remoção.