【问题标题】:JPA @OneToMany -> Parent - Child Reference (Foreign Key)JPA @OneToMany -> 父 - 子参考(外键)
【发布时间】:2012-03-20 23:29:15
【问题描述】:

我有一个关于从子实体 ir 引用 ParentEntities 的问题 如果我有这样的事情:

父.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)

好的,到目前为止,一切都很好。但是当谈到使用 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)

因为我没有将父级设置为子级。所以我的问题:

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

父.java:

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

    this.children = children;
}
...

编辑:

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

父.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);
}

Reference 由 Parent 正确设置:

Parent:
     id
     100

Child
     id     paren_id
     101    100

【问题讨论】:

  • 你的子实体有父级吗?如果有,他们是如何配置的。没有父我得到错误(StaleStateException:批量更新从更新[0]返回了意外的行数;实际行数:0;预期:1)。如果我添加没有注释的父级,它也会显示错误(映射错误)。通过在子项的父字段上设置@manytoone,我在保存时遇到错误(ORA-00904: "REPORT_REPORT_ID": недопустимый идентификатор)。我的 fk 及其在名为 report_id 的 ID 上的 pk 但我不知道 report_report_id 来自哪里。
  • (使用 EclipseLink JPA 实现)。值得注意的是,您仍然需要 @Column(name="paren_id")private long parenId;映射到您的孩子以完成这项工作。

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


【解决方案1】:

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

这是一种策略,是的。

在双向关系中,关系有“拥有”和“非拥有”的一面。因为在您的情况下,拥有方位于Child,您需要在那里设置关系以使其保持不变。拥有方通常由您指定@JoinColumn 的位置确定,但看起来您并没有使用该注释,因此很可能是从您在Parent 注释中使用mappedBy 的事实推断出来的。

你可以read a lot more about this here

【讨论】:

    【解决方案2】:

    似乎仍然如此。在父 Entity 你可以有类似的东西

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

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

    【讨论】:

    • !!我坚持了差不多 2 周后的好人。我尝试从客户端发送 json 并使用 spring 的 @RequestBody 转换为对象,然后保存到 db 我不想自己管理关系(将父级设置为子级 - 对我来说没有意义)
    • !!!更新 !!!关于@PrePersist 的链接(您可以通过@EntityListener 进行管理)参见--concretepage.com/java/jpa/…
    • 好人!!这也节省了很多精力并优化了性能。
    • 出于某种原因,我在@PrePersistchildren.forEach 上收到NullPointerException
    • @guneetgstar 也许你的children 是空的。先初始化?
    【解决方案3】:

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

    在 JPA 2.0 规范中是这样说的:

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

    【讨论】:

      【解决方案4】:

      我们在持久化如上图所示的简单对象图时遇到了问题。在 H2 中运行一切都可以,但是当我们针对 MySQL 运行时,子表中的“paren_id”(在 @JoinColumn 注释中定义)没有被生成的父 id 填充 - 即使它被设置为非- 数据库中具有外键约束的空列。

      我们会得到这样的异常:

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

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

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

      【讨论】:

      • Put nullable = false 也解决了我的 OneToMany 问题。但是对于我的 OneToOne 来说,我仍然会遇到异常。关于如何处理这种情况的任何建议?
      • 我也试过这个,但没有运气,如果这是问题,我也在使用 MySQL。可以分享一下代码吗?
      • 嗯,有时人们给“parent_id”起个名字并运行应用程序。运行后 db 创建约束。稍后他们更改对象上“parent_id”字段的名称并运行应用程序。我认为旧字段保留在 be 中,因为它有约束,并且会通过抛出父 ID 缺失的错误消息来误导
      【解决方案5】:

      如果我正确地告诉你,根据EntityManager,如果你希望它管理事务的插入顺序,你必须“告诉他”它也应该坚持孩子。而你没有那样做,所以“他”不知道要坚持什么,但你父母的孩子列表不是空的,所以“他”认为它是正确的,但存储的值为 null。

      所以你应该考虑做这样的事情:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-04-20
        • 2011-01-28
        • 1970-01-01
        • 2016-02-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-19
        相关资源
        最近更新 更多