问题 JPA坚持有一对多关系的父母和孩子


我想用20个子实体来保持父实体, 我的代码如下

家长班

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

儿童班

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

所以,如果有20个子表,那么我必须在每个子表中设置父引用,因为我必须写20个for循环?  这可行吗?有没有其他方式或配置,我可以自动坚持父母和孩子?


5490
2018-02-04 10:04


起源

这似乎是一个JSON问题,而不是JPA问题。如果你的JSON被解组以便设置正确的关系,那么在保存父节点时保持子节点只是将相关的级联选项添加到@OneToMany(假设你的映射是正确的) - Alan Hay
如果您没有发回Child-> Parent关系,或者没有在从JSON构建的内容中设置,那么是的,您需要在每个子实体中手动设置它。另一种方法是使关系单向:从OneToMany中删除mappedby =“parent”,而是指定JoinColumn。这将导致OneToMany在子表中设置外键而不是由子对父对象的引用设置(然后您应该删除Child的父属性和映射) - Chris


答案:


修复您的父类:

@OneToMany(mappedBy = "parent")

的mappedBy 财产应该指向关系另一边的领域。如 的JavaDoc 说:

拥有这种关系的领域。除非关系是单向的,否则是必需的。

你也应该在周期中明确地持久化Child实体:

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

艾伦海伊 注释中注意到,您可以使用级联设施,让EntityManager自动保留所有子实体:

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

有关cascades(和JPA本身)的更多详细信息,请参阅 Vlad Mihalcea的博客


6
2018-02-04 10:14



他也不需要@OneToMany的级联选项吗? - Alan Hay
但对于20个不同的儿童系列,我将不得不写20个不同的for循环? - Gora
@Bharath Reddy,我想是的,20个不同的子集合是20个不同的父对象,你将调用方法save(...)20次。 - Ivan Babanin
@BharathReddy,我已经更新了我的答案。 - Ivan Babanin
我曾使用过cascade = CascadeType.PERSIST,但是我将foregin key作为null - Gora


通常,@ JoinColumn表示实体是 这段关系的所有者 &mappedBy表示实体是 关系的逆

所以,如果你想跟随

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

这意味着它 关系的逆 并且它不会为其子项设置父引用。

要设置对其子项的父引用,您必须创建上述实体 这段关系的所有者 以下列方式。

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

您不需要设置任何子引用,因为上面的代码将在子表中创建一个列。


4
2018-05-02 05:11



这不是一个更好的方式 Unidirectional @OneToMany 然后Hibernate会再向DB创建两次SQL请求 vladmihalcea.com/2017/03/29/... - panser


我会让父母坚持自己的孩子

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
2018-02-04 10:24



正在使用 FetchType.EAGER 在我们只希望父母同样坚持孩子的情况下是必要的吗? - Stephane
@Stephane不,我想我是出于测试原因而做的 - Essex Boy


正如评论中指出的那样,您必须注意与子/父关系的对象图一致性。当JSON直接来自POST请求时,这种一致性将不会自由。

您必须使用标注父字段和子字段 @JsonBackReference 和 @JsonManagedReference

家长班:

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

儿童班:

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

回答的类似问题是 这里

此外,如果你使用 @JsonBackReference/@JsonManagedReference 上 javax.persistence 注释类与Lombok的组合 @ToString 您将在stackoverflow错误中出现的注释。

只是排除 childCollection 和 parent 来自的领域 @ToString 注释用 @ToString( exclude = ...)

龙目岛的生成也会发生同样的情况 equals() 方法 (@Data@EqualsAndHashCode)。只需手动或使用即可实现这些方法 @Getter 和 @Setter 仅注释。


2
2017-08-22 10:15



如果输入请求是XML格式,我们如何管理? - user630209