【问题标题】:Foreign key not stored in child entity (one-to-many)外键未存储在子实体中(一对多)
【发布时间】:2011-02-01 01:47:49
【问题描述】:

我对休眠很陌生,偶然发现了这个问题,我找不到解决方案。

当持久化父对象(与子对象具有一对多关系)时,该父对象的外键不会存储在子对象的表中。

我的课:

父类.java

@javax.persistence.Table(name = "PARENT")
@Entity
public class PARENT {
  private Integer id;

  @javax.persistence.Column(name = "ID")
  @Id
  @GeneratedValue(strategy=GenerationType.AUTO)
  public Integer getId() {
    return id;
  }

  public void setId(Integer id) {
    this.id = id;
  }

  private Collection<Child> children;

  @OneToMany(mappedBy = "parent", fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
  @Cascade({org.hibernate.annotations.CascadeType.ALL})
  public Collection<Child> getChildren() {
    return children;
  }

  public void setChildren(Collection<Child> children) {
    this.children = children;
  }
}

Child.java

@javax.persistence.Table(name = "CHILD")
@Entity
@IdClass(Child.ChildId.class)
public class Child {
  private String childId1;

  @Id
  public String getChildId1() {
    return childId1;
  }

  public void setChildId1(String childId1) {
    this.childId1 = childId1;
  }

  private String childId2;

  @Id
  public String getChildId2() {
    return childId2;
  }

  public void setChildId2(String childId2) {
    this.childId2 = childId2;
  }

  private Parent parent;

  @ManyToOne
  @javax.persistence.JoinColumn(name = "PARENT_ID", referencedColumnName = "ID")
  public Parent getParent() {
    return parent;
  }

  public void setParent(Operation parent) {
    this.parent = parent;
  }

  public static class ChildId implements Serializable {
    private String childId1;

    @javax.persistence.Column(name = "CHILD_ID1")
    public String getChildId1() {
      return childId1;
    }

    public void setChildId1(String childId1) {
      this.childId1 = childId1;
    }

    private String childId2;

    @javax.persistence.Column(name = "CHIILD_ID2")
    public String getChildId2() {
      return childId2;
    }

    public void setChildId2(String childId2) {
      this.childId2 = childId2;
    }

    public ChildId() {
    }

    @Override
    public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;

      ChildId that = (ChildId) o;

      if (childId1 != null ? !childId1.equals(that.childId1) : that.childId1 != null) 
        return false;
      if (childId2 != null ? !childId2.equals(that.childId2) : that.childId2 != null)
        return false;

      return true;
    }

    @Override
    public int hashCode() {
      int result = childId1 != null ? childId1.hashCode() : 0;  
      result = 31 * result + (childId2 != null ? childId2.hashCode() : 0);
      return result;
    }
  }
}

Test.java

public class Test() {

  private ParentDao parentDao;

  public void setParentDao(ParentDao parentDao) {
    this.parentDao = parentDao;
  }

  private ChildDao childDao;

  public void setChildDao(ChildDao childDao) {
    this.childDao = parentDao;
  }

  test1() {
    Parent parent = new Parent();

    Child child = new Child();
    child.setChildId1("a");
    child.setChildId2("b");
    ArrayList<Child> children = new ArrayList<Child>();
    children.add(child);

    parent.setChildren(children);
    parent.setValue("value");

    parentDao.save(parent); //calls hibernate's currentSession.saveOrUpdate(entity)
  }

  test2() {
    Parent parent = new Parent();
    parent.setValue("value");
    parentDao.save(parent); //calls hibernate's currentSession.saveOrUpdate(entity)

    Child child = new Child();
    child.setChildId1("a");
    child.setChildId2("b");
    child.setParent(parent);
    childDao.save(); //calls hibernate's currentSession.saveOrUpdate(entity)
  }
}

当调用 test1() 时,两个实体都被写入数据库,但 CHILD 表中的字段 PARENT_ID 保持为空。

到目前为止,我唯一的解决方法是 test2() - 首先是坚持父母,然后是孩子。

我的目标是在一次调用 Parent 上的 save() 时保持父级及其子级。 有什么想法吗?

【问题讨论】:

    标签: java hibernate mapping annotations


    【解决方案1】:

    您还可以为您的父母添加一个额外的方法,例如

    public void addChild(Child child) 
    

    类似

        child.setParent(this);
    

    【讨论】:

    • 太好了,实现这个方法解决了我的问题。现在我没有创建一个新数组,而是调用 parent.addChild(child) 来创建新数组(如果需要),然后将父级分配给子级。现在在父对象上调用 save() 可以像我想要的那样保存两个实体。感谢您的想法!
    • 非常感谢您的宝贵时间。你能告诉我们为什么hibernate在内部不关心这个吗?或者有什么办法可以将其移交给休眠状态。
    • 8 多年前,我认为 Hibernate 已经不复存在 :-) 我喜欢其他牧场(不一定更环保)。可能它已经改变了,但我想它会造成各种模棱两可(我是谁的父母)
    【解决方案2】:

    我有一个很好的解决方案:D

    mappedBy 表示 inverse = "true" 。所以你必须inverse = "false"

    试试吧..

    @OneToMany(fetch = FetchType.EAGER, cascade ={CascadeType.ALL})//javax.persistent.CascadeType
    @JoinColumn(name = "parent_id")//parent's foreign key
    public Collection<Child> getChildren() {
        return children;
    }
    

    希望工作顺利

    【讨论】:

    • 我将如何在 xml 中执行此操作?
    【解决方案3】:

    test1() 的主要问题是您没有在子对象中设置父对象。你应该说.. child.setParent(parent)。 只是在父母中设置孩子并不意味着孩子知道父母。事实上,它的孩子拥有关系。因此,有必要在子对象中设置父对象。 对父或子调用 saveorUpdate() 的顺序无关紧要。

    test2() 正在工作,因为您通过调用 child.setParent() 在子对象中设置了父对象。 test1() 中缺少这个。

    【讨论】:

      【解决方案4】:

      问题是,在 test1() 中,您在保存父级之前先保存了子级。在 test2() 中,您首先保存父级,然后是子级,这是正确的,也是它起作用的原因。

      这样想。你能在你父母出生之前就出生吗?不,它们必须先存在,然后你才能存在。就像在 test2() 中一样,先创建父级,然后是子级。

      如果你愿意,你可以编辑你的 parentDAO.save() 来保存或更新任何引用的孩子。这样,他们只需 1 次调用即可保存。

      【讨论】:

      • 感谢您的解释。我认为hibernate会处理这样的操作。在查看 test1() 的日志和休眠插入/更新时,首先插入的是父项,然后是子项。正如我所看到的 - 休眠应该足够“智能”以获取 Id 表单插入父项,然后将其分配给子项并执行插入。总结一下 - 在我想为一个父母插入 50 个孩子的情况下,我必须为每个孩子调用 save() 吗?您确定没有其他方法可以让休眠“自动”执行吗? :)
      • 您可以在您的 DAO 中编写一个函数来保存子列表。
      • 我认为这不是真的。如果你启用 show-sql 你可以看到父母首先被保存
      【解决方案5】:

      在您保存父级之前,尚未为其分配 ID。因此,当您先保存孩子时,它没有要保存的外键。你想像 test2() 那样做,但是包含在一个事务中。看看here。我认为标题为“使用 EJB/CMT 划分事务”的部分可能是您想要的。

      【讨论】:

      • 正如我在上面的评论中解释的那样,在查看 test1() 的休眠日志时,首先插入了父项,然后是子项。所以hibernate应该足够聪明,从第一个插入中获取id并将其分配给第二个?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-15
      • 2015-10-13
      • 2011-11-28
      • 1970-01-01
      • 2019-11-03
      相关资源
      最近更新 更多