0 Replies Latest reply on Jan 3, 2008 5:09 PM by jseriff

    EJB 3.0 Persistence with Parent/Child and composite keys

    jseriff

      I have a fairly common scenario which I am having trouble getting to work as I expect. I have searched through the documentation and forums, but have not yet found any information which helped be with my issue.

      I have two tables, lets call them PARENT and CHILD in a MySQL database.

      ------------------
      | PARENT |
      ------------------
      | PARENT_ID (PK) |
      | <attributes> |
      ------------------
      
      ------------------
      | CHILD |
      ------------------
      | PARENT_ID (PK) |
      | CHILD_ID (PK) |
      | <attributes> |
      ------------------
      


      I also have entity classes for these two tables -

      @Entity
      @Table(name="parent")
      public class Parent implements Serializable {
       @Id
       @GeneratedValue
       @Column(name="parent_id")
       private int parentId;
      
       @OneToMany(cascade={CascadeType.ALL})
       @JoinColumn(name="parent_id")
       private List<Child> children;
      
       //getters,setters,etc.
      }
      
      @Entity
      @Table(name="child")
      public class Child implements Serializable {
       @EmbeddedId
       private ChildId childId;
      
       //getters, setters, hashCode, equals, etc
      }
      
      @Embeddable
      public class ChildId implements Serializable {
       @Column(name="parent_id")
       private int parentId;
      
       @Column(name="child_id")
       private int childId;
      
       //getters, setters, hashCode, equals, etc.
      }
      


      So far, this seems pretty straight forward, and I believe gives me what I want.

      If I create a new Parent object, add child objects and merge, it appears to save, although in a strange fashion. (Note: I am using merge, as in the general case, these may or may not be new objects).

      The (paraphrased) SQL which is generated using hibernate is -

      insert on PARENT.
      insert on CHILD set PARENT_ID = 0 and CHILD_ID = <correct child_id>.
      update on CHILD set PARENT_ID = <correct parent_id> and CHILD_ID = <correct child_id> where PARENT_ID = 0 and CHILD_ID = <correct child_id>
      


      This does appear to get the job done, but I run into a major problem when I try to insert two parent_ids where child_id has the same value. For example, if I have a Parent1 with a Child1 and a Child2 and a Parent2 with a Child2 (different object, same CHILD_ID) and a Child3 I get the following statements (paraphrased)-

      insert on PARENT.
      insert on PARENT.
      insert on CHILD set PARENT_ID = 0 and CHILD_ID = 1
      insert on CHILD set PARENT_ID = 0 and CHILD_ID = 2
      insert on CHILD set PARENT_ID = 0 and CHILD_ID = 2 (not sure why this doesn't fail... i believe its done via a batch insert?)
      insert on CHILD set PARENT_ID = 0 and CHILD_ID = 3
      update on CHILD set PARENT_ID = 1 where PARENT_ID = 0 and CHILD_ID = 1
      update on CHILD set PARENT_ID = 1 where PARENT_ID = 0 and CHILD_ID = 2
      update on CHILD set PARENT_ID = 2 where PARENT_ID = 0 and CHILD_ID = 2
      update on CHILD set PARENT_ID = 2 where PARENT_ID = 0 and CHILD_ID = 3
      


      It then throws an exception because the update expected to change one row, and it changed none (the third update statement has no effect).

      I am using JBoss 4.2 and SEAM 2.0 out of the box.

      I am not clear what I am doing wrong. I assume that the insert with parent_id is 0 is because there is no parent_id set on the child object. I would also assume that the persistence manager would be responsible for setting this after the parent objects are inserted.

      I would prefer not to have a backwards reference from the child object back to the parent object, as this seems redundant, and will never be used in this context. Am I missing an annotation, or annotation parameter somewhere which will get me the expected behavior? (I would expect to see only 2 parent and 4 child inserts, with no updates at all). Am I forced to flush the persistence manager between each merge(parent) call?

      Thanks for your help,
      Jason