Erro de Hibernate: org.hibernate.NonUniqueObjectException: um objeto diferente com o mesmo valor de identificador já estava associado à sessão

114

Eu tenho dois objetos de usuário e enquanto tento salvar o objeto usando

session.save(userObj);

Eu estou recebendo o seguinte erro:

Caused by: org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session:
[com.pojo.rtrequests.User#com.pojo.rtrequests.User@d079b40b]

Estou criando a sessão usando

BaseHibernateDAO dao = new BaseHibernateDAO();          

rtsession = dao.getSession(userData.getRegion(),
                           BaseHibernateDAO.RTREQUESTS_DATABASE_NAME);

rttrans = rtsession.beginTransaction();
rttrans.begin();

rtsession.save(userObj1);
rtsession.save(userObj2);

rtsession.flush();
rttrans.commit();

rtsession.close(); // in finally block

Eu também tentei fazer o session.clear()antes de salvar, ainda sem sorte.

Esta é a primeira vez que estou obtendo o objeto de sessão quando chega uma solicitação do usuário, então estou entendendo por que está dizendo que o objeto está presente na sessão.

Alguma sugestão?

duro
fonte
Aqui está outro tópico maravilhoso que ajudou a resolver meu problema getj2ee.over-blog.com/…
Reddymails

Respostas:

173

Já tive esse erro muitas vezes e pode ser muito difícil rastreá-lo ...

Basicamente, o que o hibernate está dizendo é que você tem dois objetos que possuem o mesmo identificador (mesma chave primária), mas não são o mesmo objeto.

Eu sugiro que você divida seu código, ou seja, comente os bits até que o erro desapareça e, em seguida, coloque o código de volta até que ele volte e você encontre o erro.

Na maioria das vezes acontece por salvamento em cascata, onde há um salvamento em cascata entre o objeto A e B, mas o objeto B já foi associado à sessão, mas não está na mesma instância de B que o de A.

Qual gerador de chave primária você está usando?

A razão de eu perguntar é que esse erro está relacionado a como você está dizendo ao hibernate para verificar o estado persistente de um objeto (ou seja, se um objeto é persistente ou não). O erro pode estar acontecendo porque o Hibernate está tentando persistir um objeto que já é persistente. Na verdade, se você usar o save, o hibernate tentará persistir esse objeto, e talvez já exista um objeto com a mesma chave primária associada à sessão.

Exemplo

Supondo que você tenha um objeto de classe de hibernação para uma tabela com 10 linhas com base em uma combinação de chave primária (coluna 1 e coluna 2). Agora, você removeu 5 linhas da tabela em algum momento. Agora, se você tentar adicionar as mesmas 10 linhas novamente, enquanto o hibernate tenta persistir os objetos no banco de dados, 5 linhas que já foram removidas serão adicionadas sem erros. Agora, as 5 linhas restantes que já existem, irão lançar esta exceção.

Então a abordagem mais fácil seria verificar se você atualizou / removeu algum valor de uma tabela que faz parte de algo e mais tarde você está tentando inserir os mesmos objetos novamente

Michael Wiles
fonte
4
Boa resposta². A chave primária era meu problema, resolvido com o GeneratedValue configurando uma sequência para o postgresql.
Rodrigo Ferrari
2
Eu tive o mesmo problema. No meu caso, eu tinha um objeto pesquisado em um código e estava tentando construir um novo objeto com a mesma ID em outro trecho de código enquanto o primeiro objeto ainda estava na sessão de hibernação.
dellasavia
18

Este é apenas um ponto em que a hibernação causa mais problemas do que resolve. No meu caso existem muitos objetos com o mesmo identificador 0, pois são novos e não possuem nenhum. O db os gera. Em algum lugar eu li que 0 sinais não foram definidos. A maneira intuitiva de persisti-los é iterar sobre eles e dizer hibernar para salvar os objetos. Mas você não pode fazer isso - "É claro que você deve saber que hibernar funciona desta e daquela maneira, portanto, você deve ..." Portanto, agora posso tentar alterar os Ids para Longos em vez de longos e ver se funciona. No final, é mais fácil fazer isso com um mapeador simples por conta própria, porque hibernar é apenas um fardo intransparente adicional. Outro exemplo: Tentar ler parâmetros de um banco de dados e persisti-los em outro força você a fazer quase todo o trabalho manualmente.

Hein
fonte
13
Odeio hibernar também ... e bancos de dados ... Depois de todos os problemas que eles me causaram, acho que é mais fácil usar um arquivo texto (estou brincando, mas mesmo assim ...).
Igor Popov de
No meu caso existem muitos objetos com o mesmo identificador 0, pois são novos e não possuem nenhum. O db os gera. Em algum lugar eu li que 0 sinais não foram definidos. A maneira intuitiva de persisti-los é iterar sobre eles e dizer hibernar para salvar os objetos . Eu preciso fazer exatamente isso. Você poderia me dizer qual é a "maneira de hibernar" de fazer isso?
Ramses
No meu caso, tive que usar session.merge (myobject), pois tinha duas instâncias desse objeto. Normalmente isso acontece quando uma entidade é persistida e separada da sessão. Outra instância dessa entidade é solicitada para hibernar. Essa segunda instância permaneceu vinculada à sessão. A primeira instância é modificada. Mais em getj2ee.over-blog.com/…
Reddymails
não é hibernar causando problemas, mas falta de compreensão de como funciona
ACV
17

Uso session.evict(object);A função de evict()método é usada para remover a instância do cache de sessão. Portanto, para salvar o objeto pela primeira vez, salve-o chamando o session.save(object)método antes de remover o objeto do cache. Da mesma forma, atualize o objeto chamando session.saveOrUpdate(object)ou session.update(object)antes de chamar evict ().

Rahul N
fonte
11

Isso pode acontecer quando você usa o mesmo objeto de sessão para leitura e gravação. Quão? Digamos que você tenha criado uma sessão. Você lê um registro da tabela de funcionários com a chave primária Emp_id = 101 Agora você modificou o registro em Java. E você vai salvar o registro do funcionário no banco de dados. não fechamos sessão em nenhum lugar aqui. Como o objeto que foi lido também persiste na sessão. Ele está em conflito com o objeto que desejamos escrever. Daí vem esse erro.

Prashant Bari
fonte
9

Como alguém já apontou acima, encontrei esse problema quando tinha as cascade=allduas pontas de um one-to-manyrelacionamento, então vamos supor A -> B (um para muitos de A e muitos para um de B) e estava atualizando a instância de B em A e, em seguida, chamando saveOrUpdate (A), estava resultando em uma solicitação de salvamento circular, ou seja, salvamento de A aciona salvamento de B que aciona salvamento de A ... e na terceira instância como a entidade (de A) foi tentada ser adicionado a sessionPersistenceContext a exceção duplicateObject foi lançada.
  Eu poderia resolver isso removendo a cascata de uma extremidade.

Redzedi
fonte
Resolvi meu problema junto com stackoverflow.com/questions/4334970/…
Magno C de
5

Você pode usar session.merge(obj), se estiver fazendo save com diferentes sessões com o mesmo objeto persistente identificador.
Funcionou, eu tive o mesmo problema antes.

Pavan Kumar
fonte
4

Também tive esse problema e tive dificuldade em encontrar o erro.

O problema que tive foi o seguinte:

O objeto foi lido por um Dao com uma sessão de hibernação diferente.

Para evitar essa exceção, simplesmente releia o objeto com o dao que salvará / atualizará este objeto posteriormente.

tão:

class A{      

 readFoo(){
       someDaoA.read(myBadAssObject); //Different Session than in class B
    }

}

class B{



 saveFoo(){
       someDaoB.read(myBadAssObjectAgain); //Different Session than in class A
       [...]
       myBadAssObjectAgain.fooValue = 'bar';
       persist();
    }

}

Espero que isso economize muito tempo para algumas pessoas!

Frithjof
fonte
4

Encontrei este problema por:

  1. Excluir um objeto (usando HQL)
  2. Armazenar imediatamente um novo objeto com o mesmo id

Resolvi esvaziando os resultados após a exclusão e limpando o cache antes de salvar o novo objeto

String delQuery = "DELETE FROM OasisNode";
session.createQuery( delQuery ).executeUpdate();
session.flush();
session.clear();
wbdarby
fonte
4

Esse problema ocorre quando atualizamos o mesmo objeto da sessão, que usamos para buscar o objeto do banco de dados.

Você pode usar o método de mesclagem de hibernação em vez do método de atualização.

por exemplo, primeiro use session.get () e então você pode usar session.merge (objeto). Este método não criará nenhum problema. Também podemos usar o método merge () para atualizar o objeto no banco de dados.

Jayendra Birtharia
fonte
3

Pegue o objeto dentro da sessão, aqui um exemplo:

MyObject ob = null;
ob = (MyObject) session.get(MyObject.class, id);
sock_osg
fonte
3
Boa tentativa, mas o objeto "ob" é retornado sem os dados atualizados (considerando que foi atualizado através da aplicação durante o tempo de execução, entre a recuperação dos objetos do banco de dados e seu salvamento).
Alex
2

Seus mapeamentos de ID estão corretos? Se o banco de dados é responsável por criar o Id por meio de um identificador, você precisa mapear seu objeto de usuário para esse.

cwap
fonte
2

Eu encontrei este problema com a exclusão de um objeto, nem despejar nem limpar ajudou.

/**
 * Deletes the given entity, even if hibernate has an old reference to it.
 * If the entity has already disappeared due to a db cascade then noop.
 */
public void delete(final Object entity) {
  Object merged = null;
  try {
    merged = getSession().merge(entity);
  }
  catch (ObjectNotFoundException e) {
    // disappeared already due to cascade
    return;
  }
  getSession().delete(merged);
}
TimP
fonte
2

antes da posição onde os objetos repetitivos começam, você deve fechar a sessão e então você deve iniciar uma nova sessão

session.close();      
session = HibernateUtil.getSessionFactory().openSession();

portanto, desta forma em uma sessão não há mais de uma entidade com o mesmo identificador.

Engtuncay
fonte
2

Atrasado para a festa, mas pode ajudar para os próximos usuários -

Eu tenho esse problema quando seleciono um registro usando getsession() e atualizo novamente outro registro com o mesmo identificador usando a mesma sessão que causa o problema. Código adicionado abaixo.

Customer existingCustomer=getSession().get(Customer.class,1);
Customer customerFromUi;// This customer details comiong from UI with identifer 1

getSession().update(customerFromUi);// Here the issue comes

Isso nunca deve ser feito. A solução é remover a sessão antes da atualização ou alterar a lógica de negócios.

vipin cp
fonte
1

Verifique se você esqueceu de colocar @GenerateValue para a coluna @Id. Eu tive o mesmo problema com muitos para muitos relacionamentos entre Filme e Gênero. O programa lançou Erro de Hibernate: org.hibernate.NonUniqueObjectException: um objeto diferente com o mesmo valor de identificador já estava associado ao erro de sessão. Descobri depois que só preciso ter certeza de que você tem @GenerateValue para o método de obtenção GenreId.

Thupten
fonte
como posso aplicar sua solução à minha pergunta? faço para criar uma coluna id na classe Task? stackoverflow.com/questions/55732955/…
sbattou
1

apenas verifique o id se leva nulo ou 0 como

if(offersubformtwo.getId()!=null && offersubformtwo.getId()!=0)

em adicionar ou atualizar onde o conteúdo é definido do formulário para Pojo

Ayan Sarkar
fonte
1

Eu sou novo no NHibernate, e meu problema é que usei uma sessão diferente para consultar meu objeto da que usei para salvá-lo. Portanto, a sessão de salvamento não sabia sobre o objeto.

Parece óbvio, mas lendo as respostas anteriores, eu estava procurando em todos os lugares por 2 objetos, não 2 sessões.

DustinA
fonte
1

@GeneratedValue (strategy = GenerationType.IDENTITY), adicionar esta anotação à propriedade da chave primária em seu bean de entidade deve resolver esse problema.

Jay
fonte
1

Eu resolvi esse problema.
Na verdade, isso está acontecendo porque esquecemos a implementação do tipo de gerador da propriedade PK na classe do bean. Então faça de qualquer tipo como

@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;

quando persistimos os objetos do bean, todos os objetos adquirem o mesmo ID, então o primeiro objeto é salvo, quando outro objeto a ser persistido então o HIB FW através deste tipo de Exception: org.hibernate.NonUniqueObjectException:objeto diferente com o mesmo valor de identificador já estava associado à sessão.

Ankit Sharma
fonte
1

O problema ocorre porque na mesma sessão de hibernação você está tentando salvar dois objetos com o mesmo identificador. Existem duas soluções: -

  1. Isso está acontecendo porque você não configurou seu arquivo mapping.xml corretamente para os campos de id conforme abaixo: -

    <id name="id">
      <column name="id" sql-type="bigint" not-null="true"/>
      <generator class="hibernateGeneratorClass"</generator>
    </id>
  2. Sobrecarregue o método getsession para aceitar um parâmetro como isSessionClear e limpe a sessão antes de retornar à sessão atual como abaixo

    public static Session getSession(boolean isSessionClear) {
        if (session.isOpen() && isSessionClear) {
            session.clear();
            return session;
        } else if (session.isOpen()) {
            return session;
        } else {
            return sessionFactory.openSession();
        }
    }

Isso fará com que os objetos de sessão existentes sejam apagados e mesmo que o hibernate não gere um identificador único, assumindo que você configurou seu banco de dados corretamente para uma chave primária usando algo como Auto_Increment, ele deve funcionar para você.

Rahul Kumar
fonte
1

Eu tive um problema parecido. No meu caso, esqueci de definir oincrement_by valor no banco de dados para ser igual ao usado por cache_sizee allocationSize. (As setas apontam para os atributos mencionados)

SQL:

CREATED         26.07.16
LAST_DDL_TIME   26.07.16
SEQUENCE_OWNER  MY
SEQUENCE_NAME   MY_ID_SEQ
MIN_VALUE       1
MAX_VALUE       9999999999999999999999999999
INCREMENT_BY    20 <-
CYCLE_FLAG      N
ORDER_FLAG      N
CACHE_SIZE      20 <-
LAST_NUMBER     180

Java:

@SequenceGenerator(name = "mySG", schema = "my", 
sequenceName = "my_id_seq", allocationSize = 20 <-)
kaba713
fonte
1

Ao contrário do que wbdarby disse, pode até acontecer quando um objeto é buscado fornecendo o identificador do objeto a um HQL. No caso de tentar modificar os campos do objeto e salvá-lo novamente no BD (modificação pode ser inserir, excluir ou atualizar) na mesma sessão , este erro aparecerá. Tente limpar a sessão de hibernação antes de salvar o objeto modificado ou crie uma nova sessão.

Espero ter ajudado ;-)

Arash moradabadi
fonte
1

Eu tenho o mesmo erro ao substituir meu conjunto por um novo obtido de Jackson.

Para resolver isso, mantenho o conjunto existente, removo do conjunto antigo o elemento desconhecido para a nova lista com retainAll. Em seguida, adiciono os novos com addAll.

    this.oldSet.retainAll(newSet);
    this.oldSet.addAll(newSet);

Não há necessidade de ter a Sessão e manipulá-la.

Ôrel
fonte
1

Experimente isso. O abaixo funcionou para mim!

No hbm.xmlarquivo

  1. Precisamos definir o dynamic-updateatributo da tag de classe para true:

    <class dynamic-update="true">
  2. Defina o atributo de classe da tag do gerador na coluna exclusiva para identity:

    <generator class="identity">

Observação: defina a coluna exclusiva como em identityvez de assigned.

RaGa__M
fonte
0

Outra coisa que funcionou para mim foi tornar a variável de instância Long no lugar de long


Eu tinha minha variável de chave primária long id; alterando-o para Long id; trabalhou

Muito bem sucedida


fonte
0

Você sempre pode fazer um flush de sessão. Flush irá sincronizar o estado de todos os seus objetos na sessão (por favor, alguém me corrija se eu estiver errado), e talvez isso resolveria o seu problema em alguns casos.

Implementar seus próprios equals e hashcode também pode ajudá-lo.

caarlos0
fonte
0

Você pode verificar as configurações do Cascade. As configurações de Cascade em seus modelos podem estar causando isso. Eu removi as configurações do Cascade (essencialmente não permitindo inserções / atualizações do Cascade) e isso resolveu meu problema

user1609848
fonte
0

Eu também encontrei esse erro. O que funcionou para mim foi ter certeza de que a chave primária (que é gerada automaticamente) não é um PDT (ou seja, longo, int, ect.), Mas um objeto (ou seja, Long, Integer, etc.)

Ao criar seu objeto para salvá-lo, certifique-se de passar null e não 0.

Jaco Van Niekerk
fonte
0

Isso ajuda?

User userObj1 = new User();
User userObj2 = userObj1;
.
.
.
rtsession.save(userObj1);
rtsession.save(userObj2); 
George Papatheodorou
fonte
0

Resolvi um problema semelhante como este:

plan = (FcsRequestPlan) session.load(plan.getClass(), plan.getUUID());
while (plan instanceof HibernateProxy)
    plan = (FcsRequestPlan) ((HibernateProxy) plan).getHibernateLazyInitializer().getImplementation();
user3132194
fonte