2016-02-04 18 views
6

chcę utrzymywać jednostkę macierzystą z 20 jednostek podrzędnych, mój kod jest poniżejJPA Utrzymują rodzica i dziecko z jednego do wielu relacji

klasa nadrzędna

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

klasa Child

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

Więc jeśli istnieje 20 tabel podrzędnych, to muszę ustawić nadrzędną referencję w każdym z nich, ponieważ muszę napisać 20 dla pętli? Czy to możliwe? czy istnieje jakakolwiek inna metoda lub konfiguracja, w której mogę automatycznie utrzymać rodzica i dziecko?

+0

Wydaje się, że jest to bardziej pytanie o JSON niż pytanie WZP. Jeśli Twój JSON zostanie usunięty, aby ustalić właściwe relacje, to posiadanie dzieci po zapisaniu rodzica jest po prostu kwestią dodania odpowiednich opcji kaskadowych do @OneToMany (zakładając, że twoje odwzorowania są poprawne) –

+2

Jeśli nie wysyłasz dziecka -> Relacja rodzica z powrotem, lub nie jest ustawiona w tym, co jest zbudowane z JSON-a, tak, musisz ręcznie ustawić to w każdym elemencie podrzędnym. Alternatywą jest uczynienie relacji jednokierunkową: usuń zmapowany = "rodzic" z OneToMany i zamiast tego podaj wartość JoinColumn. Spowoduje to, że OneToMany ustawi klucz obcy w tabeli podrzędnej zamiast być ustawiane przez odniesienie potomka do jego obiektu nadrzędnego (wtedy należy usunąć atrybut i odwzorowanie rodzica potomnego). – Chris

Odpowiedz

5

Poprawianie klasy nadrzędnej:

@OneToMany(mappedBy = "parent") 

mappedBy nieruchomość powinna być skierowana do pola po drugiej stronie relacji. Jako JavaDoc jest napisane:

Pole będące właścicielem związku. Wymagane, chyba że związek jest jednokierunkowy.

Ponadto należy jawnie utrzymują podmiot dziecko w cyklu:

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

Jak Alan Hay zauważył w komentarzu, można korzystać z urządzeń kaskadowych i niech EntityManager automatycznie utrzymują wszystkie podmioty dziecka:

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

Więcej szczegółów na temat kaskad (i samej JPA) można znaleźć w Vlad Mihalcea's blog.

+0

Czy nie będzie potrzebował opcji kaskadowych w tym @OneToMany? ? –

+0

, ale w przypadku 20 różnych kolekcji dla dzieci będę musiał napisać 20 różnych pętli? – Gora

+0

@Bharath Reddy, wydaje mi się, że tak, ponieważ 20 różnych kolekcji dla dzieci to 20 różnych obiektów nadrzędnych, a wywołasz metodę zapisywania (...) 20 razy. –

2

chciałbym niech rodzice utrzymują swoje własne dzieci

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

Jak wskazano w uwagach należy zadbać obiektu wykresu zgodności z relacji dziecka/macierzystej. Ta spójność nie będzie wolna, gdy JSON będzie pochodził bezpośrednio z, to jest, żądania POST.

Należy dodać adnotację do pola nadrzędnego i podrzędnego za pomocą @JsonBackReference i @JsonManagedReference.

klasa nadrzędna: Klasa

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

dziecka:

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

Podobne pytanie z odpowiedzią jest here

Ponadto, jeśli używasz @JsonBackReference/@JsonManagedReference na javax.persistence adnotacjami klas w połączeniu z Lombok @ToString adnotacja, którą poniosą Państwo w stackoverflow erro r.

Wystarczy wykluczyć childCollection i parent Pole z @ToString z dopiskiem @ToString(exclude = ...)

To samo stanie się z Lombok generowała equals() metodę (@Data, @EqualsAndHashCode). Wystarczy wdrożyć te metody ręcznie lub użyć tylko adnotacji @Getter i @Setter.

3

Generalnie @JoinColumn wskazuje, że podmiot jest właścicielem relacji & mappedBy wskazuje, że jednostka jest odwrotność stosunku.

Więc jeśli starają się jak po

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

to znaczy, że jest odwrotność stosunku i nie będzie ustawić odniesienie do jego rodzica dziecka.

Aby ustawić nadrzędny odnośnik do jego potomka, należy podać powyższy podmiot: właściciel relacji w następujący sposób.

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

Nie trzeba ustawiać żadnych odniesień potomnych, ponieważ powyższy kod spowoduje utworzenie kolumny w tabeli podrzędnej.

+0

nie jest to lepszy sposób, ponieważ nazywa się 'Unidirectional @ OneToMany', a następnie Hibernate stworzy dwa razy więcej zapytań SQL do DB https://vladmihalcea.com/2017/03/29/the-best-way-to-map-a-onetomany-association-with-jpa-and-hibernate / – panser

Powiązane problemy