Hibernate Detached and Merge
Autor: Marcos Oliveira | Publicado: 2010-12-22T09:18:00-08:00

Consideramos que um objeto é Detached quando ele estava em um estado persistente, mas sua sessão foi fechada. É comum isso acontecer em aplicações, principalmente ao alterar objetos, suponha a seguinte situação: Você efetua a listagem de determinada entidade e seleciona uma delas para alterar, ou apenas informa um código da entidade que deseja alterar. Então para abrir a tela de alteração você terá que consultar a entidade no banco e exibir as informações atuais na tela. Quando você faz isso você carrega a entidade em uma sessão Hibernate, provavelmente usando o método load ou get e fecha a sessão ao exibir esses dados para alteração. Quando a entidade é submetida para ser alterada ela é considerada Detatched.   A entidade Detached um dia já fez parte de outro ‘grupo’ (sessão hibernate antiga), mas não faz parte desta (sessão hibernate atual). Ela é diferente de uma entidade Persistent, que é uma entidade que está na sessão e tem identificador válido dentro da sessão atual.   Esse é um estado enjoado. Considere a situação relatada acima. Se você for usar o método save ou update para persistir essa entidade você pode se deparar com alguns problemas. O Hibernate só admite em sua sessão no máximo 1 objeto por ID. E se você já tiver uma entidade carregada na sessão atual com o mesmo identificador da entidade Detatched que você está submetendo, você provavelmente irá receber isso:   "nested exception is org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session:..."   Essa exceção seria o mesmo que o Hibernate, que é o comandante do ‘grupo’, dizer: “Você não pode entrar nesse grupo, já existe alguém aqui com essa identificação!!”.   Veja que fantástico isso é para entender um pouco do funcionamento do Hibernate. Provavelmente você só passou os olhos em cima de um item interessantíssimo, então vou repetí-lo: "O Hibernate só admite em sua sessão no máximo 1 instância de classe por ID.". Apenas um objeto por classe e ID*. Por isso o get(Class, Serializable) é como se fosse um mapa. Como se fosse, não é um mapa.   Então você deverá usar algumas táticas para contornar isso. Uma solução seria atribuir os dados alterados na instância da sessão atual, por exemplo:   //Obtendo a entidade ativa (a entidade com identificador válido dentro da sessão)Entidade entidadeAtiva = sess.load(Entidade.class, entidadeDetached.getId());     //Atribuindo alterações que você deseja persistir na entidade ativaentidadeAtiva.setValor1(entidadeDetached.getValor1());  entidadeAtiva.setValor2(entidadeDetached.getValor2());  sess.save(entidadeAtiva);     Assim as alterações feitas na entidade Detatched podem ser sincronizadas com a entidade ativa. Porém nesse caso poderia ser utilizado o método merge() que faz exatamente o mesmo só que automaticamente. Veja que simples:     sess.merge(entidadeDetached);     Segundo a Documentação oficial do Hibernate, o comportamento do merge é o seguinte:     • Se houver uma instância persistente com o mesmo identificador atualmente associado com a sessão, copia o estado do objeto para o estado da instância persistente  • Se não houver nenhuma instância persistente atualmente associada com a sessão, tenta carregá-lo a partir do banco de dados, ou criar uma nova instância persistente  • a instância persistente é retornada  • a instância dada não é associada com a sessão, continua detatched.     O merge então nos salva desse problema, mas como vimos no artigo, poderemos ter outros pepinos para nos preocupar. Mas esse do Detached agora você já sabe.   *Em outro artigo poderemos falar sobre mapeamento de entidades e verá como utilizar a notação @Id para definir um atributo como identificador da entidade.  Referências:  http://docs.jboss.org/hibernate/core/3.3/reference/en/html/index.html  http://docs.jboss.org/hibernate/entitymanager/3.5/reference/en/html/objectstate.html#d0e1318