2009-04-29 5 views
2

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, permettez-moi d'exposer ce que j'essaie d'accomplir, au cas où mes problèmes de mon approche.JPA EntityManger préformant une insertion de base de données lors de la fusion?

J'ai des données sur le système que j'ai besoin de placer dans un autre système. Pour ce faire, je suis en train de lire les données puis de construire de nouveaux objets ORM en fonction de ces données, je veux ensuite conserver les objets ORM dans la base de données. Maintenant, lorsque la base de données est vide, le programme fonctionne sans problème, avec un simple appel em.persist (objet). Cependant, je veux être en mesure d'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 j'ai des problèmes.

Je fais une vérification simple 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'. 

Il me semble étrange que l'appel em.merge() tente un insert au lieu d'une mise à jour (je confirme cela par la journalisation SQL).

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

Je devrais noter que j'utilise des objets qui cascadent dans des sous-objets. L'échec se produit sur un sous-objet, ce qui est logique pour moi car je m'attendrais à essayer de fusionner les sous-objets en premier. Ci-dessous est mon code, s'il vous plaît excusez l'état approximatif, j'ai essayé de documenter quelques approches 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 ayant 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 évidemment 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 pour votre temps.

+0

Pouvez-vous publier comment vous avez mappé l'association de 'stuff' à 'subStuffCollection'? – zmf

+0

Stuff est un à plusieurs à sous-soumettre. L'annotation est @OneToMany (cascade = CascadeType.ALL, mappedBy = "stuffId", fetch = FetchType.LAZY). –

Répondre

3

La malédiction de StackOverflow a encore frappé. Après avoir travaillé sur ce problème pendant environ une journée, je décide de poster cette question, dans les 20 minutes, j'ai eu un moment d'eureka et je l'ai résolu. En partie parce que j'avais suffisamment clarifié mes pensées pour poster la question. En pensant à quelle information pourrait être pertinente à la question, je me suis rendu compte que mes clés générées automatiquement étaient à blâmer (ou mon correctement, mon stupide non-manipulation d'entre eux sur une fusion).

Mon problème est que SubStuff (pire schéma de dénomination de rechange, désolé à ce sujet) a une clé primaire artificielle autogénérée. Donc, lors de la fusion, j'avais besoin de faire;

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

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

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

L'appel à fusionner peut être simplifié jusqu'à l'appel à em.fusionner (stuff) car cela mettra en cascade les objets de substuff et la collection mergedsubstuff peut être supprimée tous ensemble car nous ne faisons plus de fusion dans cette boucle.

Merci à tous ceux qui ont lu ma question ridiculement longue, j'espère que ma question sera utile à quelqu'un dans le futur.

+0

Je suis content que vous l'ayez compris. – topchef

1

Votre problème est avec le couple suivant des lignes:

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

JPA examinera la nouvelle collection aussi complète nouvelle série de sous-entités et toujours les insérer en tant que tel. Continuez à travailler avec la collection originale de SubStuff dans l'entité Stuff et tout ira bien.

+0

Il n'arrive même pas à la deuxième ligne. Il échoue sur "SubStuff mergedSubStuff = em.merge (f);" (Souhaitez SO pris en charge les numéros de ligne de code). Votre conseil sera probablement utile dans le futur, merci –

+0

La collection mergedSubStuff peut être complètement supprimée du code, elle est hautaine. La chose à importer est que mon SubStuff soit assigné la clé primaire correcte, alors il sera fusionné correctement par la cascade faite avec l'appel à em.persist (substance). –

+0

"hautain"? Superflu. –

Questions connexes