7 Replies Latest reply on Oct 3, 2007 10:59 PM by waynebaylor

    Working example por OneToMany cascade remove?

    icordoba

      Hi there,
      I've spent days reading threads and googling about the behaviour of the remove cascade in EJB3 but haven't been able to make Cascade.REMOVE to work in a OneToMany relation.
      Could anybody post a working example? I have, for example, Portals and Users. I just need that when I do a em.remove(portal), all its users are removed. For the moment I have to remove the child entities myself... and I guess that this should be done by the cascade remove.

      I am getting that famous "deleted entity passed to persist" exception.

      Thanks for any example,
      Ignacio

        • 1. Re: Working example por OneToMany cascade remove?
          waynebaylor

          i think it's safe to assume that you have the "mappedBy" attribute set for the User side of the relationship, right?

          the mappedBy attribute basically says which side of the relationship is responsible for maintaining the DB foreign key link. so, you could try this in your existing code:

          User[] users = portal.getUsers();
          for(User user : users)
          {
           user.setPortal(null); //or user.removePortal(), whatever your method is
          }
          
          em.remove(portal);
          



          • 2. Re: Working example por OneToMany cascade remove?
            icordoba

            Hi there,
            thanks a lot for your reply... this is the Portal side:

            @OneToMany(mappedBy="portal",fetch=FetchType.LAZY, cascade={CascadeType.ALL})
             public void setUsers(Set<User> users)
             {
             this.users = users;
             }
            
             @OneToMany(mappedBy="portal",fetch=FetchType.LAZY, cascade={CascadeType.ALL})
             public Set<User> getUsers()
             {
             return users;
             }
            


            and this is the user side:

             @ManyToOne
             public Portal getPortal()
             {
             return portal;
             }
            
             @ManyToOne
             public void setPortal(Portal portal)
             {
             this.portal = portal;
             }
            


            Does it look right?

            Ignacio

            • 3. Re: Working example por OneToMany cascade remove?
              waynebaylor

              first thing is that you only need the annotation on the get*() methods.

              try setting the portal to null for each user before you actually remove the portal.

              • 4. Re: Working example por OneToMany cascade remove?
                icordoba

                OK.. thanks for the reply.
                This way, Users will the orphan and I need the special Hibernate annotation for orphans to be removed, right?

                Is there a way of making JBoss really remove User entities itself without that Hibernate-specific annotation?

                I can't understand what is then the pourpose of the CascadeType.REMOVE (or ALL in this case) as I must "unlink" users from portals setting Portal value to null before removing the portal. Does cascading do any work then?

                Thanks a lot,
                Ignacio

                • 5. Re: Working example por OneToMany cascade remove?
                  waynebaylor

                  here's an example i just tested:

                  @Entity
                  public class Parent
                  {
                   @Id
                   @GeneratedValue
                   private Long id;
                  
                   @OneToMany(mappedBy="parent", cascade=CascadeType.REMOVE)
                   private Set<Child> children = new HashSet<Child>();
                  
                   public Set<Child> getChildren()
                   {
                   return children;
                   }
                  
                   public void setChildren(Set<Child> children)
                   {
                   this.children=children;
                   }
                  
                   public Long getId()
                   {
                   return id;
                   }
                  }
                  ==========================
                  
                  
                  @Entity
                  public class Child
                  {
                   @Id
                   @GeneratedValue
                   private Long id;
                  
                   @ManyToOne
                   private Parent parent;
                  
                   private String name;
                  
                   public Child(){}
                  
                   public Child(String name)
                   {
                   this.name = name;
                   }
                   ...
                  }
                  


                  and here is the code that creates and removes the entities:
                  @Stateless
                  @Local(TesterLocal.class)
                  @LocalBinding(jndiBinding="session.Tester/local")
                  public class Tester implements TesterLocal
                  {
                   @PersistenceContext(unitName="PU2")
                   private EntityManager em;
                  
                   public void doWork()
                   {
                   Parent p = new Parent();
                   em.persist(p);
                  
                   for(int i=0; i<5; ++i)
                   {
                   Child c = new Child("child-"+i);
                   c.setParent(p);
                   p.getChildren().add(c);
                  
                   em.persist(c);
                   }
                   em.flush();
                   p = null;
                  
                   Parent pp = em.find(Parent.class, 1L); //i know the id is 1
                   em.remove(pp);
                   em.flush();
                   }
                  }


                  this code does not throw any exceptions. checking the DB i can see that the parent and all children are removed.

                  • 6. Re: Working example por OneToMany cascade remove?

                    So is it a fair statement to say that delete cascading does not and will not work on OneToMany relationships? How inconvenient.

                    We're using a stateful session bean (Seam) which allows us to keep changing the object model until the user is ready to either a) save or b) cancel. On a cancel its easy - we just walk away from everything. On a save, our goal was to simply end the conversation in the default manner, which has Hibernate flush all of the changes.

                    It sounds like we're going to have to keep track of the before/after state, descend through all of the OneToManys, to comparisons, and manually handle everything. It seems odd that CascadeType.REMOVE doesn't do this, since it certainly could follow the same logic - that's why I'm still hoping that we're missing some setting somewhere...?

                    • 7. Re: Working example por OneToMany cascade remove?
                      waynebaylor

                      the above post is a working example of cascading delete.