2017-10-04 2 views
0

J'utilise les données de printemps jpa. J'ai relation un à plusieurs défini comme ceci:JPA remplacer un élément dans une collection

@OneToMany(mappedBy = "participant", cascade = CascadeType.ALL, orphanRemoval = true) 
    public List<SurveyResponse> getSurveyResponses() { 
     return surveyResponses; 
    } 

J'ai une méthode qui prend une collection de surveyResponses et remplace repsonses existants qui ont une clé correspondante. Malheureusement, lorsque je l'utilise, Hibernate supprime toutes les réponses autres que celles que j'ajoute. Pourquoi cela se passe-t-il?

Je ne sauvegarde jamais les réponses directement, je les mets juste dans la collection et laisse cascade faire le reste. Voici la méthode qui fait le remplacement:

public void updateResponses(List<SurveyResponse> responses) { 
     for (SurveyResponse response : responses) { 
      response.setParticipant(this); 
     } 
     if (this.surveyResponses != null) { 
      Set<String> questionKeys = responses.stream() 
        .map(SurveyResponse::getQuestionKey) 
        .collect(Collectors.toSet()); 
      this.surveyResponses.removeIf(
        surveyResponse -> questionKeys.contains(surveyResponse.getQuestionKey())); 
      this.surveyResponses.addAll(responses); 
     } else this.surveyResponses = responses; 
    } 

Après l'addAll appeler le PersistentBag a un mélange d'ancien (géré) et nouveau (pas encore persisté) réponses. Lorsque j'appelle save (méthode jpa crudRepository de données de printemps) sur l'entité participant, toutes les anciennes réponses sont supprimées. Pourquoi?

+0

Êtes-vous sûr à 100% que tout n'est pas retiré de la collection après removeIf()? Oui! vous l'avez mentionné mais avez-vous vraiment vu si dans le débogueur vous avez toujours des réponses attachées après removeIf()? –

+0

Oui, j'ai couru ceci dans le débogueur, j'ai regardé comme le PersistentBag a eu un élément plus petit après le removeIf, il a eu un élément plus grand après le addAll. – JacobSTL

Répondre

0

Cette question devrait être supprimée, car c'était un bug ailleurs dans mon code. J'ai fait un setter trop compliqué qui causait le problème. Supprimer des questions avec des réponses est déconseillé, alors je réponds à ma propre question ici. Pour la curiosité de quelqu'un, ce fut le mauvais compositeur:

public void setSurveyResponses(List<SurveyResponse> surveyResponses) { 
    if (this.surveyResponses == null) { 
     this.surveyResponses = surveyResponses; 
    } else { 
     this.surveyResponses.clear(); 
     if (surveyResponses != null) { 
      this.surveyResponses.addAll(surveyResponses); 
     } 
    } 
    if (this.surveyResponses != null) { 
     for (SurveyResponse response : this.surveyResponses) { 
      response.setParticipant(this); 
     } 
    } 
} 

Note à moi, setters sont pour mise en veille prolongée à utiliser, non pas pour votre propre code. Si vous avez besoin d'un fantaisiste, faites une nouvelle méthode.

0

Cela pourrait être la cause de votre problème: http://assarconsulting.blogspot.com/2009/08/why-hibernate-does-delete-all-then-re.html

Je suggère de remplacer le type de collecte de List à Set et voir comment cela se comporte.

+0

Je serais heureux si Hibernate a fait un tout supprimer et réinsérer tout dans la collection en mémoire, mais il est seulement d'insérer ce que j'ai ajouté à la collection et de ne pas réinsérer les éléments que je n'ai pas touchés – JacobSTL

+1

Utilisez Set pour vous amuser mais ça n'a pas fait de différence. Je suppose que le problème est que je ne respecte pas la règle: «Un objet géré ne doit jamais faire référence à d'autres objets gérés, et un objet détaché ne doit faire référence qu'à d'autres objets détachés. aux problèmes, car votre application peut accéder à deux copies du même objet causant la perte de modifications ou de données obsolètes. " mentionné ici: https://en.wikibooks.org/wiki/Java_Persistence/Persisting#Detached_vs_Managed – JacobSTL