Pergunta

Eu estou usando JPA com Hibernate embaixo, e eu estou tendo problemas para obter mesclagem para trabalho, mas antes de eu descrever os problemas que estou encontrando com JPA, deixe-me colocar para fora o que estou tentando realizar, apenas no caso de meus problemas decorre da minha abordagem.

Eu tenho dados sobre o sistema que eu preciso lugar em um outro sistema. Para fazer isso eu estou lendo os dados, em seguida, construir novo ORM objetos com base nesses dados, então eu quiser manter os objetos ORM no banco de dados. Agora, quando o banco de dados estiver vazia, o programa funciona sem problema, com uma simples chamada em.persist (objeto). No entanto eu quero ser capaz de executar esse processo quando o banco de dados tem dados nele, acrescentando novos dados e atualizar dados antigos, se necessário, este é o lugar onde eu estou tendo problemas.

Eu faço uma simples verificação para ver se o item já existe no banco de dados, se nada for encontrado, eu persistirem, se existe o registro, eu tentar uma fusão. Que falha com um erro de registro duplicado;

ERROR JDBCExceptionReporter - Violation of UNIQUE KEY constraint 'UK-SubStuff-StuffId-SubStuffNumber'. Cannot insert duplicate key in object 'SubStuff'.

Parece-me estranho que o em.merge () chamada está tentando uma inserção em vez de uma atualização (I confirmada através da exploração madeireira SQL).

Hibernate: insert into SubStuff (SubStuffNumber, StuffId, Name, TypeId) values (?, ?, ?, ?)

Devo observar que eu estou usando objetos que cascata em sub-objetos. A falha está ocorrendo em um sub-objeto, o que faz sentido para mim como eu esperaria que para tentar mesclar o sub-objetos em primeiro lugar.

Abaixo está o meu código, por favor, desculpe o estado áspero, eu tentei documentar alguns solução alternativa se aproxima aqui.

private void storeData(Collection<Stuff> Stuffs) {

    for (Stuff stuff : Stuffs) {
        //I think this first block can be safely ignored, as I am having no issues with it
        //  Left it in just in case someone more experianced then I sees the root of the issue here.
        Collection<SubStuff> subStuffs = stuff.getSubStuffCollection();
        for (SubStuff s : subStuffs) {
            //Persist SubStuff Type, which DOES NOT cascade,
            //  due to it not having an internal SubStuff collection
            Query q = em.createNamedQuery("SubStuffType.findByType");
            q.setParameter("type", f.getTypeId().getType());
            try {
                SubStuffType sst = (SubStuffType) q.getSingleResult();
                s.setTypeId(sst);
            } catch (NoResultException ex) {
                if (logger.isDebugEnabled()) logger.debug("SubStuff Type not found, persisting");
                em.persist(s.getTypeId());
            }
        }

        if (em.find(Stuff.class, stuff.getId()) == null) {
            //Persist on Stuffs will cascade to SubStuffs
            em.persist(stuff);
        } else {
            //  Failing to merge SubStuff, tries to insert duplicate
            //  Merge SubStuff first
            // The block below is my attempt to merge the SubStuff Collection before merging Stuff,
            //  it creates the same isuse as a straight merge of Stuff.
            Collection<SubStuff> mergedSubStuffs = new ArrayList<SubStuff>(SubStuffs.size());
            for (SubStuff s : SubStuffs) {
                Query q = em.createNamedQuery("SubStuff.findBySubStuffNumberStuffId");
                q.setParameter("SubStuffNumber", s.getSubStuffNumber());
                q.setParameter("StuffId", stuff.getId());
                try {
                    SubStuff subStuff = (SubStuff) q.getSingleResult();
        // -----> Merge fails, with an duplicate insert error
                    SubStuff mergedSubStuff = em.merge(s);
                    mergedSubStuffs.add(mergedSubStuff);
                } catch (NoResultException ex) {
                    throw ex;
                }
            }
            stuff.setSubStuffCollection(mergedSubStuffs);

        // -----> This will fails with same error as above, if I remove the attempt
            //  to merge the sub objects
            em.merge(stuff);
        }
    }
}

Se alguém com experiência JPA pode me ajudar eu realmente aprecio isso. Os differances entre saveOrUpdate do Hibernate () e merge de JPA () são, obviamente, tropeçar me up, mas, apesar de ler vários artigos sobre fusão de EnityManger, eu ainda não posso envolver minha cabeça em torno do que está acontecendo aqui.

Obrigado pelo seu tempo.

Foi útil?

Solução

A maldição de StackOverflow atingiu novamente. Depois de trabalhar sobre este problema por aproximadamente um dia eu decidir postar esta questão, em 20 minutos eu tive um momento eureka e resolveu. Em parte porque eu tinha esclarecido meus pensamentos suficientes para postar a pergunta. Ao pensar sobre que informação pode ser pertinente a pergunta que eu percebi que meus auto chaves geradas eram os culpados (ou o meu corretamente, meu estúpido não-manipulação deles em um merge).

Meu problema é que SubStuff (pior regime alternativo de nomeação, desculpe sobre isso) tem uma chave primária artificial gerada automaticamente. Então, quando a fusão que eu precisava fazer;

SubStuff subStuff = (SubStuff) q.getSingleResult();
//++++++++
s.setId(subStuff.getId());
//++++++++

//The following code can be removed.
//--- SubStuff mergedSubStuff = em.merge(f);
//--- mergedSubStuffs.add(mergedSubStuff);

Isso define a chave primária para a linha que já existe no banco de dados e sobre o teste inicial parece funcionar bem.

A chamada para merge pode ser simplificada para baixo a apenas a chamada para em.merge (coisas) como que vai em cascata os objetos substuff ea coleção mergedsubstuff podem ser removidos todos juntos, como não estamos mais fazendo uma mesclagem dentro daquele loop.

Graças a quem ler o meu ridiculamente longa pergunta, espero que a minha pergunta será útil para alguém no futuro.

Outras dicas

Seu problema é com o seguinte par de linhas:

Collection<SubStuff> mergedSubStuffs = new ArrayList<SubStuff>(SubStuffs.size());
...
stuff.setSubStuffCollection(mergedSubStuffs);

JPA irá considerar nova coleção tão completa novo conjunto de sub-entidades e será sempre inseri-los como tal. Continue trabalhando com coleção original de SubStuff dentro entidade material e você vai ficar bem.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top