web-dev-qa-db-fra.com

JPA Persiste parent et enfant avec une relation un à plusieurs

Je souhaite conserver l'entité parent avec 20 entités enfant, mon code est ci-dessous

Classe parent

@OneToMany(mappedBy = "parentId")
private Collection<Child> childCollection;

Classe enfant

@JoinColumn(name = "parent_id", referencedColumnName = "parent_id")
@ManyToOne(optional=false)
private Parent parent;

String jsonString = "json string containing parent properties and child  collection" 

ObjectMapper mapper = new ObjectMapper();
Parent parent = mapper.readValue(jsonString, Parent.class);

public void save(Parent parent) {
    Collection<Child> childCollection = new ArrayList<>() ;

    for(Child tha : parent.getChildCollection()) { 
        tha.setParent(parent);
        childCollection.add(tha);
    }

    parent.setChildCollection(childCollection);
    getEntityManager().persist(parent);
 }

Donc, s'il y a 20 tables enfants, je dois définir la référence parent dans chacune d'elles pour que je dois écrire 20 pour les boucles? Est-ce faisable? y a-t-il une autre manière ou configuration où je peux automatiquement persister parent et enfant?

18
Gora

Fixez votre classe Parent:

@OneToMany(mappedBy = "parent")

La propriété mappedBy doit pointer vers un champ de l'autre côté de la relation. Comme JavaDoc dit:

Champ propriétaire de la relation. Obligatoire sauf si la relation est unidirectionnelle.

Vous devez également conserver explicitement l'entité enfant dans le cycle:

for(Child tha : parent.getChildCollection()) { 
    ...
    getEntityManager().persist(tha);
    ...
}

Comme Alan Hay l'a remarqué dans les commentaires, vous pouvez utiliser les fonctionnalités de cascade et laisser EntityManager automatiquement persister toutes vos entités enfants:

@OneToMany(mappedBy = "parent", cascade = CascadeType.PERSIST)

Plus de détails sur les cascades (et JPA lui-même) que vous pouvez trouver dans le blog de Vlad Mihalcea .

10
Ivan Babanin

Généralement, @JoinColumn indique que l'entité est le propriétaire de la relation & mappedBy indique que l'entité est le inverse de la relation.

Donc, si vous essayez de suivre

@OneToMany(mappedBy = "parent")
private Collection<Child> childCollection;

Cela signifie qu'il est inverse de la relation et qu'il ne définira pas la référence parent à son enfant.

Pour définir la référence parent à son enfant, vous devez créer l'entité ci-dessus propriétaire de la relation de la manière suivante.

@OneToMany(cascade = CascadeType.ALL)
@JoinColumn
private Collection<Child> childCollection;

Vous n'avez pas besoin de définir de référence enfant car le code ci-dessus créera une colonne dans la table enfant.

7
maruf571

Comme indiqué dans les commentaires, vous devez veiller à la cohérence du graphe d'objet avec la relation enfant/parent. Cette cohérence ne sera pas gratuite lorsque JSON provient directement d'une demande POST.

Vous devez annoter le champ parent et enfant avec @JsonBackReference Et @JsonManagedReference.

Classe parent:

@OneToMany(mappedBy = "parentId")
@JsonBackReference
private Collection<Child> childCollection;

Classe enfant:

@JoinColumn(name = "parent_id", referencedColumnName = "parent_id")
@ManyToOne(optional=false)
@JsonManagedReference
private Parent parent;

Une question similaire avec réponse est ici

De plus, si vous utilisez @JsonBackReference/@JsonManagedReference Sur des classes annotées javax.persistence En combinaison avec l'annotation @ToString De Lombok, vous encourrez une erreur de débordement.

Excluez simplement les champs childCollection et parent de l'annotation @ToString Avec @ToString( exclude = ...)

La même chose se produira avec la méthode equals() générée par Lombok (@Data, @EqualsAndHashCode). Implémente simplement ces méthodes à la main ou pour utiliser uniquement les annotations @Getter Et @Setter.

5
s17t.net

Je laisserais le parent persister c'est ses propres enfants

package com.greg;

import Java.util.ArrayList;
import Java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;

@Entity(name = "PARENT")
public class Parent {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(name = "NAME")
    private String name;

    @Column(name = "DESCRIPTION")
    private String description;

    @OneToMany(cascade = CascadeType.ALL, fetch=FetchType.EAGER)
    @JoinColumn(name = "parent", referencedColumnName = "id", nullable = false)
    private List<Child> children = new ArrayList<Child>();

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public List<Child> getChildren() {
        return children;
    }

    public void setChildren(List<Child> children) {
        this.children = children;
    }

}
2
Essex Boy