views:

266

answers:

3

Hi, I'm quite new to hibernate and have stumbled on this problem, which I can't find solution for.

When persisting parent object (with one-to-many relationship with child), the foreign-key to this parent is not stored in child's table.

My classes:

Parent.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)
  }
}

When calling test1(), both entities get written to database, but field PARENT_ID in CHILD table stays empty.

The only workaround I have so far is test2() - persisting parent first, and then the child.

My goal is to persist parent and its children in one call to save() on Parent. Any ideas?

A: 

The problem is, in test1(), you are saving the child before you save the parent. In test2(), you are saving the parent first, then the child, which is correct, and is why it is working.

Think about it this way. Can you be born before your mother and father are born? No, they must exist before you can exist. Crate the parent, then the child, as you do in test2().

If you want, you can edit you parentDAO.save() to save or update any referenced children. That way, they save with 1 call.

Kevin Crowell
Thank you for explanation. I thought that hibernate will take care of such operations. While looking at logs and hibernate inserts/updates for test1() there was insert for Parent first, and then for Child. As I see it - hibernate should be 'smart' enough to get the Id form inserting Parent and then assing it to Child and perform insert.So summarizing - in a scenario where I want to insert 50 children for one parent, I have to call save() for each one of them? Are you sure there is no other way for hibernate to do it 'automagically'? :)
Kamil Łoś
You can write a function in your DAO to save a List of Children.
Kevin Crowell
A: 

Before you save the parent, it hasn't been assigned an ID. So, when you save the child first, it has no foreign key to save. You want to do it like test2(), but wrap in in a transaction. Take a look here. I think the section titled "Transaction demarcation with EJB/CMT" may be what you want.

Rob Heiser
As I explained in the comment above, while looking at hibernate logs for test1() there was insert for Parent first, and then for the Child. So hibernate should be smart enough to det the id from the first insert and assign it to the second one?
Kamil Łoś
A: 

You can also add an extra method to your parent like addChild(Child child) which does something like child.setParent(this).

dr jerry
Excellent, implementing this method solved my problem. Now instead of creating a new Array I just call _parent.addChild(child)_ where the creation of new array takes place (if necessary) and then assign the parent to the child. Now calling _save()_ on parent saves both entities just like I wanted.Thanks for the idea!
Kamil Łoś