JPA @OneToMany - >父母 - 子女参考(外键)

时间:2012-03-02 12:55:32

标签: spring hibernate jpa parent-child one-to-many

我有一个关于引用Child Entites的ParentEntities的问题 如果我有这样的事情:

Parent.java:

@Entity(name ="Parent")
public class Parent {
    @Id
    @Generate.....
    @Column
    private int id;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "parent")
    private Set<Child> children;

    simple ... getter and setter ...
}

和Child.java:

@Entity(name ="Child")
public class Child{
    @Id
    @Generate....
    @Column
    private int id;

    @ManyToOne
    private Parent parent;

    ... simple getter an setter
}

将创建以下表格:

Parent:
     int id

Child:
     int id
     int parent_id (foreign key: parent.id)

好的,到目前为止,everthings很好。但是当谈到从Java使用这个参考时,我想,你可以做这样的事情。

 @Transactional
 public void test() {
    Parent parent = new Parent();

    Child child = new Child();
    Set<Child> children = new HashSet<Child>();
    children.add(child);

    parent.setChildren(children);
    entityManager.persist(parent);
  }

在数据库中导致了这一点:

Parent:
     id
     100

Child
     id     paren_id
     101    100

但事实并非如此,你必须明确地将Parent设置为Child(我认为,框架可能会自行完成)。

所以数据库中真的是这样的:

Parent:
     id
     100

Child
     id     paren_id
     101    (null)

因为我没有将Parent设置为Child。所以我的问题:

我真的必须做某事吗?像这样?

Parent.java:

...
setChildren(Set<Child> children) {
   for (Child child : children) {
     child.setParent.(this);
   }

   this.children = children;
}
...

编辑:

根据快速回复,我能够通过在引用拥有实体上使用@JoinColumn来解决此问题。如果我们从上面拿出例子,我做了......像这样:

Parent.java:

  @Entity(name ="Parent")
    public class Parent {
        @Id
        @Generate.....
        @Column
        private int id;

        @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
        @JoinColumn(name= "paren_id")
        private Set<Child> children;

        simple ... getter and setter ...
    }

和Child.java:

@Entity(name ="Child")
public class Child{
    @Id
    @Generate....
    @Column
    private int id;

    ... simple getter an setter
}

现在,如果我们这样做:

 @Transactional
 public void test() {
    Parent parent = new Parent();

    Child child = new Child();
    Set<Child> children = new HashSet<Child>();
    children.add(child);

    parent.setChildren(children);
    entityManager.persist(parent);
  }

参考由父母正确设置:

Parent:
     id
     100

Child
     id     paren_id
     101    100

感谢答案。

5 个答案:

答案 0 :(得分:14)

  

我真的必须做某事吗?像这样?

这是一种策略,是的。

在双向关系中,关系存在“拥有”和“非拥有”的一面。因为您案例中的拥有方位于Child,您需要在那里设置关系以使其保持不变。拥有方通常由您指定@JoinColumn的位置决定,但它看起来不像您正在使用该注释,因此很可能是因为您在{{mappedBy中使用了Parent这一事实。 1}}注释。

你可以read a lot more about this here

答案 1 :(得分:3)

是的,就是这样。 JPA不关注实体图的一致性。特别是你必须将它设置为双向关系的所有者方(在你的情况下是Child的父属性)。

在JPA 2.0规范中,用以下词语表示:

  

请注意,应用程序负责   保持运行时关系的一致性 - 例如,   确保双向的“一”和“多”方面   申请时,关系是彼此一致的   在运行时更新关系。

答案 2 :(得分:2)

似乎仍然如此。在父Entity中,您可以使用

@PrePersist
private void prePersist() {
   children.forEach( c -> c.setParent(this));
}

为了避免重复代码在代码中的其他位置设置子/父关系。

答案 3 :(得分:1)

我们遇到一个问题,同时坚持像上面所示的简单对象图。在H2中运行一切都会有效,但是当我们针对MySQL运行时,&#34; paren_id&#34;在子表中(在@JoinColumn注释中定义)没有填充生成的父ID - 即使它被设置为DB中具有外键约束的非空列。

我们得到这样的例外:

org.hibernate.exception.GenericJDBCException: Field 'paren_id' doesn't have a default value

对于可能遇到此问题的其他任何人,我们最终发现,我们必须使用@JoinColumn的另一个属性才能使其发挥作用:

@JoinColumn(name="paren_id", nullable=false)

答案 4 :(得分:0)

如果我按照EntityManager的说法正确找到您,如果您希望它管理交易的插入顺序,您必须告诉他&#34;它应该坚持孩子们。而你并没有这样做,所以&#34;他&#34;我不知道要坚持什么,但你父母的孩子名单不是空的,所以&#34;他&#34;取得正确,但存储的值为空。

所以你应该考虑做类似的事情:

... begin, etc
em.persist(child)
em.persist(parent)

在这里使用父对象做你想做的事情然后提交,这也适用于类似的情况。