Question

J'utilise JPA avec Hibernate en dessous et j'ai du mal à faire fonctionner la fusion, mais avant de décrire les problèmes que je rencontre avec JPA, laissez-moi exposer ce que je cherche à accomplir, juste au cas où mes problèmes découleraient de mon approche.

J'ai des données d'un système que je dois placer dans un autre système. Pour ce faire, je lis les données, puis je construis de nouveaux objets ORM à partir de ces données. Je souhaite ensuite conserver les objets ORM dans la base de données. Désormais, lorsque la base de données est vide, le programme fonctionne sans problème, avec un simple appel em.persist (objet). Toutefois, je souhaite pouvoir exécuter ce processus lorsque la base de données contient des données, en ajoutant de nouvelles données et en mettant à jour les anciennes données si nécessaire. C’est là que je rencontre des problèmes.

Je fais une simple vérification pour voir si l'élément existe déjà dans la base de données. Si rien n'est trouvé, je persiste, si l'enregistrement existe, je tente une fusion. Qui échoue avec une erreur d'enregistrement en double;

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

Cela me semble étrange que l'appel em.merge () tente une insertion au lieu d'une mise à jour (je l'ai confirmé via la journalisation SQL).

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

Je dois noter que j'utilise des objets qui se transforment en sous-objets. L'échec se produit sur un sous-objet, ce qui me semble logique, car je m'attendrais à ce qu'il tente de fusionner les sous-objets en premier.

Ci-dessous, mon code. Veuillez excuser l’état brut, j’ai essayé de documenter quelques solutions de contournement ici.

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);
        }
    }
}

Si quelqu'un avec une expérience JPA peut m'aider, je l'apprécierais vraiment. Les différences entre saveOrUpdate () de Hibernate et merge () de JPA me font trébucher, mais malgré la lecture de plusieurs articles sur la fusion d'EnityManger, je ne peux toujours pas comprendre ce qui se passe ici.

Merci de votre temps.

Était-ce utile?

La solution

La malédiction de StackOverflow a encore frappé. Après avoir travaillé sur ce problème pendant environ un jour, j'ai décidé de poster cette question. En 20 minutes, j'ai eu un moment de crise et je l'ai résolu. En partie parce que j'avais suffisamment clarifié mes pensées pour poser la question. En réfléchissant aux informations qui pourraient être pertinentes pour la question, je me suis rendu compte que mes clés générées automatiquement étaient à blâmer (ou bien, ma stupide non-manipulation de ces clés lors d’une fusion).

Mon problème est que SubStuff (le pire système de nommage de substitution, désolé pour ce motif) possède une clé primaire artificielle auto-générée. Donc, lors de la fusion, je devais le faire;

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

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

Ceci définit la clé primaire sur la ligne qui existe déjà dans la base de données et semble bien fonctionner lors du test initial.

L’appel à la fusion peut être simplifié jusqu’à appeler à em.merge (matériel), ce qui entraînera la cascade des objets de substitution et la collection de mergedsubstuff pourra être supprimée dans l’ensemble, car nous ne ferons plus de fusion à l’intérieur de cette boucle.

Merci à tous ceux qui ont lu ma question ridiculement longue, espérons que ma question sera utile à quelqu'un à l'avenir.

Autres conseils

Votre problème concerne les lignes suivantes:

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

JPA considérera la nouvelle collection comme un nouvel ensemble complet de sous-entités et les insérera toujours telles quelles. Continuez à travailler avec la collection originale de SubStuff dans l’entité Stuff et tout ira bien.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top