3 Replies Latest reply on Mar 30, 2007 10:57 AM by fhh

    CascadeType is misbehaving!

    justinmiller

      I am seeing something very weird with respect to cascading. There are really two problems. Let's say that I have classes A and B, and then a main method below that:

      
      @Entity
      @Table(name="A")
      public class A {
      
       @Column(name="ID")
       private String id;
      
       @OneToMany(cascade= {CascadeType.REMOVE, CascadeType.REFRESH, CascadeType.PERSIST})
       @JoinColumn(name="PARENT_ID")
       private Collection<B> children;
      
      
       public A() {
       super();
       }
      
       public A(String id) {
       this.id = id;
       }
      
       public Collection<B> getChildren() {
       return children;
       }
      
      
       public void setChildren(Collection<B> children) {
       this.children = children;
       }
      
       public String getId() {
       return id;
       }
      
       public void setId(String id) {
       this.id = id;
       }
      
      }
      


      
      @Entity
      @Table(name="B")
      public class B implements Serializable {
      
       @Column(name="ID")
       private String id;
      
       @Column(name="PARENT_ID")
       private String parent;
      
       public B() {
       super();
       }
      
       public B(String id) {
       this.id = id;
       }
      
       public String getId() {
       return id;
       }
      
       public void setId(String id) {
       this.id = id;
       }
      
       public String getParent() {
       return parent;
       }
      
       public void setParent(String parent) {
       this.parent = parent;
       }
      
      }
      

      
      public class EJB3Test {
      
       public static void main(String[] args) {
       A a = new A(generateId());
       B b = new B(generateId());
      
       a.setChildren(Arrays.asLis(new B[] { b }));
       b.setParent(a.getId());
      
       //for argument's sake, let's just say SessionFacade is just a wrapper
       //around EntityManager.persist/merge/remove
       SessionFacade.persist(a);
      
       a = new A(a.getId());
      
       //let's say we've changed 'a' somehow, and we want those changes
       //persisted, but we WANT it's relationship to it's children
       //preserved.
       SessionFacade.merge(a);
       }
      }
      


      The first problem:
      Notice how I don't have CascadeType.MERGE set on the relationship between A and it's children. Why then, when I do the merge(), will it attempt to set B.PARENT_ID to null? I would think that if it's not set to cascade on a merge, the EntityManager shouldn't even touch children. I have found however, that setting updatable=false on the JoinColumn causes the expected behavior.

      The second problem is an extension of the first:
      Let's take it one step further, assuming we set updatable=false, and right before we try SessionFacade.merge(a), we do this:

      a.setChildren(Arrays.asList(new B[] { new B(someNewId) } ));
      
      //then do:
      SessionFacade.merge(a)
      


      The merge now throws an exception, even though it's NOT set to cascade on merge, and children is set to updatable=false. The exception we get is an EntityNotFoundException stating that it can't find an object B with id: someNewId. Well no kidding! It's not there! I know it seems weird to NOT want to cascade, but I assure you - in my context, I simply need more control over how the children are inserted. Therefore, it makes perfect sense to have children that I do not want persisted until I explicitly do so.

      I shouldn't have to grab a reference to the children, null out the field in A, and then merge all the children.
      Collection<B> children = a.getChildren();
      a.setChildren(null);
      
      SessionFacade.merge(a);
      
      for(B child : children)
       SessionFacade.merge(child);
      


      ANY help would be greatly appreciated!!! This is driving me crazy!