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

时间:2016-02-04 10:04:28

标签: jpa one-to-many

我希望将父实体与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循环?  这可行吗?有没有其他方法或配置,我可以自动坚持父母和孩子?

5 个答案:

答案 0 :(得分:8)

修复您的Parent类:

@OneToMany(mappedBy = "parent")

mappedBy 属性应指向关系另一侧的字段。正如JavaDoc所说:

  

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

此外,您应该在周期中明确地保留Child实体:

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

在评论中注意到Alan Hay,您可以使用级联设施,让EntityManager自动保留所有子实体:

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

您可以在Vlad Mihalcea's blog中找到有关级联(和JPA本身)的更多详细信息。

答案 1 :(得分:7)

通常,@ JoinColumn表示该实体是关系的所有者&amp; mappedBy表示实体是关系的反转

所以,如果你想跟随

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

这意味着它是关系的反转,并且它不会设置对其子项的父引用。

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

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

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

答案 2 :(得分:3)

正如评论中指出的那样,您必须注意与子/父关系的对象图一致性。当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;

回答的类似问题是here

此外,如果您在@JsonBackReference带注释的类中使用@JsonManagedReference / javax.persistence并结合Lombok的@ToString注释,则会出现stackoverflow错误。

只需使用childCollection

parent注释中排除@ToString@ToString( exclude = ...)字段

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

答案 3 :(得分:2)

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

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

}

答案 4 :(得分:0)

我正在使用lombok在我的实体类上生成getter和setter属性。 当我尝试保存有孩子的父实体时,我也面临子实体NULL引用ID的问题。 当我添加子代时,在我的父代实体上,然后在子代上设置父代的“ this”引用。 在我的示例中,我有一个用户表和一个地址表,其中一个用户可以有很多地址。
我创建了如下的域类。

例如address.setUser(this);

package com.payment.dfr.entities;

import lombok.Data;

import javax.persistence.*;
import java.math.BigInteger;
@Entity
@Data
@Table(name="User")
public class User {

    @Id
    @GeneratedValue
    private BigInteger RecordId;
    private String Name;
    private String Email;

    @Getter(AccessLevel.NONE)
    @Setter(AccessLevel.NONE)
    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
    private List<Address> addresses = new ArrayList<>();

    public void addAddress(Address address){
        address.setUser(this);
        addresses.add(address);
    }
    
}

@Entity
@Data
@Table(name="UserAddress")
public class Address {

    @Id
    @GeneratedValue
    private BigInteger RecordId;
    private String AddressLine;
    private String City;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name="UserId")
    private User user;

}


This is how I save user with address 

    User newUser = new User();
    newUser.setName("Papa");
    newUser.setEmail("manish@gmail.com");
    Address address1 = new Address();
    address1.setAddressLine("4401 Central Ave");
    address1.setCity("Fremont");
    newUser.addAddress(address1);
    Address address2 = new Address();
    address2.setAddressLine("4402 Central Ave");
    address2.setCity("Fremont");

    newUser.addAddress(address2);
    User user1 = userRepository.save(newUser);
    log.info(user1.getRecordId().toString());