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.
Sua palestra tão elogiada será disponibilizada?
Comment by Luca Bastos — November 24, 2006 @ 6:01 am
Olá apesar da grande quantidade de textos sobre hibernate/JPA, eu acho extremamente válido, é só ver no guj, hibernate responde por uma fatia considerável das dúvidas diárias (até acho q deveria ter um tópico só para hibernate, hehe), então qq ajuda é sempre bem vinda nesse assunto.
Continuem assim!
Comment by Edufa — November 24, 2006 @ 7:22 am
“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”
Muito boa, por sinal. Pena que não peguei desde o começo. PS: A Caelum já ta com curso baseada nela né? Não? Tão esperando o que?
Comment by Diego Pires Plentz — November 26, 2006 @ 1:24 pm
No curso as entidades são mapeadas com as anotações do javax.persistence, mas ainda usamos Session/SessionFactory !
Comment by Fabio Kung — November 29, 2006 @ 5:58 am
Só fiquei com uma dúvida. Você está mostrando que, para recuperar um objeto detached, você usou o merge, mas o merge também pode ser utilizado para fazer um update em um objeto, portanto, ao utilizarmos para recuperar, como ele vai saber que não deve ser feito também o update?
Comment by Pamela — March 10, 2007 @ 9:33 am
Muito boa introdução, parabens!
Amigo eu gostaria de saber o que eu devo fazer quando quero recuperar um objeto que já foi alterado mas ainda não foi enviado para o banco, da forma que ele está no banco. Ou seja: eu quero comparar o objeto da memória com o que está no banco, mas quando eu faço uma consulta no entityManager ele me retorna uma referencia para o objeto da memorio… assim eu não consigo acessar o que está no banco… como eu faço isso??
Comment by Jean Michel Baldessar — May 27, 2008 @ 5:35 pm
jean, chame o .clear() no seu entitymanager, assim na proxima invocacao ele nao vai tirar do 1st level cache
Comment by Paulo Silveira — May 27, 2008 @ 5:40 pm
massa d+++++ !!!!!!!!!!!!!! para que serve msm ???
achei bonito o codigo,,, só isso
Comment by bunny — November 24, 2008 @ 12:29 am
Como dúvida:
Cliente c = em.find(Cliente.class, 1);
c.setNome(“Cliente com nome alterado”);
Não faz a função do merge?
Comment by diego — March 17, 2009 @ 8:05 am
Diego, infelizmente nao faz. Toda informação que estava nao objeto o qual a referência
Cliente capontava antes dofindseria perdidade nesse caso.Comment by Paulo Silveira — March 17, 2009 @ 8:09 am
Sim sim, o que eu quis dizer não é a referência antiga, e sim trazer um novo cliente gerenciado e alterar o nome, já que só foi isso alterado..
Comment by Diego — March 17, 2009 @ 12:15 pm
Algo como
Cliente c = [1, "Diego", 22] ( id = 1, nome = “Diego”, idade = 22]
Desejo alterar o nome para Diego Raphael;
Cliente client = em.find(Cliente.class, 1);
client.setNome(“Diego Raphael”);
Não é o mesmo que:
Cliente cliente = new Cliente();
cliente.setId(1);
cliente = em.merge(cliente);
cliente.setNome(“Diego Raphael”);
Comment by Diego — March 17, 2009 @ 12:32 pm
Oi, sem duvida Diego. É o mesmo sim.
Acho que faltou eu dar a motivacao para mostrar a necessidade do merge, e ele só vai ser realmente necessario quando tivermos uma instancia detached com informacoes novas e precisamos refleti-las no BD. Imagine que o cliente estivesse com mais atributos ja populados do que apenas o ID, como por exemplo o endereco, mas voce nem sabe mais quais dados estao novos e quais nao (no caso de vir de um wizard na web, isso acontece sempre):
Cliente cliente = em.find(Cliente.class, 1);
cliente.setEndereco(" endereco novo" );
// depois de alguns requests na web, preenchendo o wizard, voce quer atualiza-lo e ainda alterar mais alguns dados.
// lembre-se que o cliente aqui ja esta detached ja que usamos Open-Session-In-View
cliente = em.merge(cliente);
cliente.setNome(”Diego Raphael”);
Agora ele estara com endereço e nome atualizados. Se voce fizesse find novamente, perderia o endereço, claro.
Comment by Paulo Silveira — March 19, 2009 @ 2:06 am
Entendi Paulo,
Se faz útil a utilização do merge no caso de um POJO vinculado ao ManagedBean, onde será alterado as informações mostrada pelo jsf, ou algo do gênero.
Abraços!
Comment by Diego Raphael — March 23, 2009 @ 2:07 am
Diego, exemplo perfeito, ja que se o managed bean estiver configurado como session scope, e seu ciclo de vida do hibernate for request, as entidades referenciadas pelo managed bean estarao detached na proxima requisicao, sendo necessario um merge, caso contrario voce perdera as informacoes atualizadas na requisicao anterior!
Comment by Paulo Silveira — March 23, 2009 @ 2:55 am
Otima explicação, de todas que li hoje essa foi a que realmente me ajudou com os conceitos.
Obrigada e parabens pelo post resumido
ehehhe
Comment by Mayara — September 17, 2009 @ 6:36 pm
[...] para ler aqui no blog da Caelum a respeito de outras exceptions frequentes no Hibernate, sobre os estados de uma entidade na JPA, hábitos importantes para todo desenvolvedor Hibernate e mapeamento de herança, além de muitos [...]
Pingback by Enfrentando a LazyInitializationException no Hibernate | blog.caelum.com.br — October 13, 2009 @ 11:25 pm
Como faço para salvar um objeto transient que já existe no banco?
Digamos que na tabela cliente eu tenha o seguinte registro:
ID = 2
Nome = “Nome antigo do cliente”
Cliente c = new Cliente();
c.setId(2);
c.setNome(“Nome novo do cliente”);
Qual comando devo executar para que o Hibernate gere o seguinte comando SQL
UPDATE Cliente SET Nome=’Nome novo do cliente’ WHERE ID=2
Ou algo similar?
Qualquer ajuda será bem-vinda!
Grato
Comment by Thiago — November 11, 2009 @ 11:15 pm
oi Thiago!
É exatamente esse caso que voce pode usar o .merge()!
abracos
Comment by Paulo Silveira — November 12, 2009 @ 11:33 am
[...] Fonte: http://blog.caelum.com.br/2006/11/23/entidades-managed-transient-e-detached-no-hibernate-e-jpa/ Frameworks, Hibernate, Java, Web [...]
Pingback by Entidades Managed, Transient e Detached no Hibernate e JPA — July 22, 2010 @ 4:21 pm