Ajuda com o hibernate - org.hibernate.NonUniqueObjectException: A different object with the same identifier value was already associated with the session

2 respostas
databasebancohibernatejava
Gilles_Pmfarias

Boa noite.

Ao tentar salvar um objeto com o hibernate, recebo a seguinte exceção: org.hibernate.NonUniqueObjectException: A different object with the same identifier value was already associated with the session.

Sei que tem um registro no banco de dados que possui a mesma PK, até aí tudo bem. O problema é que essa exceção é levantada, cai no catch e o processo (o método main da minha classe) simplesmente não pára. Ele fica rodando indefinidamente, como se estivesse dentro de um loop infinito.

Outra coisa estranha é que, antes de usar um try-catch, minha classe não mostrou que poderia ser levantada uma exceção (quando usamos objetos que levantam exceções, a IDE [netbeans, eclipse] não nos deixa compilar até tratarmos de alguma forma, correto?). Isto é, é como se eu pudesse usar as classes que estão levantando a exceção SEM colocar um try-catch. Coloquei esse try-catch para tentar pegar a exceção e tratar, mas não adiantou…

Basicamente, o problema é esse. Tento salvar um objeto com o save, ele lança a exceção (que, se puder ser tratada estaria OK), mas mesmo caindo no catch, a classe fica rodando indefinidamente, como se estivesse num loop infinito.

Se o ID do objeto não for repetido, a classe se comporta normalmente, sendo finalizada.

2 Respostas

lvbarbosa

Boa noite!

Primeiramente, uma revisão rápida sobre exceptions. Existem 3 tipos de exceções que podem acontecer:

  1. Exceções Checadas: são exceções que devem obrigatoriamente ser tratadas. O compilador vai “checar” se elas são tratadas pelo teu código. Tratar pode ser tanto um try ... catch como um throws adicionado à assinatura do método. Exemplo: IOException

  2. Exceções Não-Checadas (também chamadas de Runtime Exceptions: são exceções que não precisam ser tratadas por você obrigatoriamente. Se uma delas acontecer e não existir um try ... catch que a capture, teu método automaticamente joga ela pra cima (throw implícito). Exemplo: NullPointerException

  3. Erros: Não são exceptions em si, mas são problemas que podem acontecer durante o runtime também. Não são checados durante o compile time. Um bem famoso é o StackOverflow (o erro, não o site)

Antes de mais nada, perdão pelos termos que serão utilizados daqui em diante. Estou utilizando minha licença poética de cientista da computação.

Voltando para teu problema: A exceção throwzada pelo EntityManager, NonUniqueObjectException, como podemos observar em sua documentação, herda de RuntimeException. Ou seja: o compilador (nem a IDE) não vai te obrigar a catchá-la. Portanto, é opcional fazer o try ... catch.

E sobre o processo não morrer: experimente fechar o EntityManagerFactory antes do método terminar (pode ser no finally ) e me diz o que acontece ;). Isso tem a ver com o ServiceRegistry do java, e eu não vou tentar explicar porque nunca fui atrás pra entender como funciona por baixo dos panos. Para mim, bastou saber que devemos sempre fechar recursos abertos (quando eles não são fechados para nós, mas deu para entender). Basicamente, fica uma Thread aberta que impede a JVM de encerrar.

Exemplo:

EntityManagerFactory  emf;
try {
    emf = Persistence.createEntityManagerFactory("MinhaPersistenceUnitDefinidaNoPersistencePontoXML");
    EntityManager em = emf.createEntityManager();
    Game theGame = new Game("perdi");
    em.getTransaction().begin();
    em.persist(theGame);
    em.getTransaction().commit();
} catch (ExceptionQualquerCheckedOuUnchecked ex) {
    ex.printStackTrace();
} finally {
    if (emf != null) {
        emf.close();
    }
}

Nota: não sei se isso vai compilar, to lembrando os nomes dos métodos de cabeça.
Nota 2: um comentário maroto para talvez te fazer sentir melhor: eu também odeio tratar exceções do JPA.

Gilles_Pmfarias

Opa! Tudo bem?

Sobre os tipos de exceção, eu entendo bem. O ponto é que eu não havia entendido o porquê do processo levantar uma exceção, cair no catch e ainda assim ele não parar. Como você apontou, tem a ver com o ServiceRegistry. também não li muito a respeito, até porque não paramos para levantar documentação detalhada.

Sobre ser runtimeException, com certeza, o compilador nem o IDE sã oobrigados a pegar, concordo plenamente. Como te disse, o que achei estranho é conseguir pegar a exceção mas não conseguir fechar o processo principal.

No mais, com a tua ajuda rápida, consegui contornar o problema, fechando o EntityManagerFactory. Funcionou perfeitamente. É Como se ele ficasse trabalhando paralelamente, mesmo se estourar uma exceção, ele continua rodando e não deixa que o escopo do main feche, fica numa espécie de loop infinito. Tem de ser fechado para que esse fluxo pare.

Obrigado! (foi mal a demora pra responder)

Criado 31 de janeiro de 2017
Ultima resposta 16 de fev. de 2017
Respostas 2
Participantes 2