Domanda

Sto utilizzando JPA con Hibernate al di sotto e ho problemi a far funzionare l'unione, ma prima di descrivere i problemi che sto incontrando con l'APP, lasciatemi esporre ciò che sto cercando di realizzare, nel caso in cui i miei problemi derivino dal mio approccio.

Ho dati dal sistema che devo inserire in un altro sistema. Per fare ciò sto leggendo i dati e poi costruendo nuovi oggetti ORM basati su quei dati, quindi voglio persistere gli oggetti ORM nel database. Ora, quando il database è vuoto, il programma funziona senza problemi, con una semplice chiamata em.persist (oggetto). Tuttavia, voglio essere in grado di eseguire questo processo quando il database contiene dati, aggiungendo nuovi dati e aggiornando i vecchi dati, se necessario, è qui che ho problemi.

Faccio un semplice controllo per vedere se l'elemento esiste già nel database, se non viene trovato nulla, insisto, se il record esiste, provo a unire. Che non riesce con un errore record duplicato;

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

Mi sembra strano che la chiamata em.merge () stia tentando un inserimento anziché un aggiornamento (l'ho confermato tramite la registrazione SQL).

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

Dovrei notare che sto usando oggetti che si sovrappongono a oggetti secondari. L'errore si sta verificando su un oggetto secondario, il che ha senso per me, poiché mi aspetto che provi a fondere prima gli oggetti secondari.

Di seguito è riportato il mio codice, per favore scusa lo stato grezzo, ho provato a documentare alcuni approcci di soluzione qui.

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 qualcuno con esperienza JPA può aiutarmi, lo apprezzerei davvero. Le differenze tra saveOrUpdate () di Hibernate e merge () di JPA mi stanno ovviamente facendo inciampare, ma nonostante abbia letto diversi articoli sull'unione di EnityManger, non riesco ancora a avvolgere la mia testa su ciò che sta succedendo qui.

Grazie per il tuo tempo.

È stato utile?

Soluzione

La maledizione di StackOverflow ha colpito ancora. Dopo aver lavorato su questo problema per circa un giorno, decido di pubblicare questa domanda, in 20 minuti ho avuto un momento eureka e l'ho risolto. In parte perché avevo chiarito abbastanza i miei pensieri per pubblicare la domanda. Nel pensare a quali informazioni potrebbero essere pertinenti alla domanda, mi sono reso conto che le mie chiavi generate automaticamente dovevano essere la colpa (o la mia corretta, la mia stupida non gestione di esse su una fusione).

Il mio problema è che SubStuff (peggior schema di denominazione dei sostituti, mi dispiace per quello) ha una chiave primaria artificiale auto-generata. Quindi, durante la fusione, dovevo farlo;

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

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

Questo imposta la chiave primaria sulla riga che esiste già nel database e al momento del test iniziale sembra funzionare correttamente.

La chiamata a unire può essere semplificata fino alla sola chiamata a em.merge (roba) in quanto ciò metterà in cascata gli oggetti del substuff e la raccolta mergedsubstuff può essere rimossa tutti insieme poiché non stiamo più facendo un'unione all'interno di quel ciclo.

Grazie a chiunque abbia letto la mia domanda ridicolmente lunga, spero che la mia domanda possa essere utile a qualcuno in futuro.

Altri suggerimenti

Il tuo problema è con le seguenti righe:

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

L'APP considererà la nuova raccolta come un nuovo set completo di entità secondarie e le inserirà sempre come tali. Continua a lavorare con la raccolta originale di SubStuff all'interno dell'entità Stuff e starai bene.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top