13 Replies Latest reply on Jul 4, 2005 10:54 AM by epbernard

    Problem with removing item from collection in detached objec

    leonell

      Hello,
      there is some problem when an item from collection is removed in detached entity and entity is then updated by merge(). The deleted item remains in database.
      Tested on Preview5 and Beta1 with the same result.

      This is bug (I hope) or feature?

      Simple example of problem:

      Entity bean Order:

      package cz.qds.ejb3test.ebs;
      
      import javax.persistence.*;
      import java.util.*;
      
      @Entity
      @Table(name="orders")
      public class Order implements java.io.Serializable
      {
       private Long id;
       private Collection<OrderItem> items;
      
       @Id(generate=GeneratorType.AUTO)
       @Column(name="id",nullable=false,unique=true)
       public Long getId()
       {
       return id;
       }
       public void setId(Long id)
       {
       this.id = id;
       }
      
       @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy="order")
       public Collection<OrderItem> getItems()
       {
       return items;
       }
       public void setItems(Collection<OrderItem> items)
       {
       this.items = items;
       }
      }
      


      Entity bean OrderItem:
      package cz.qds.ejb3test.ebs;
      
      import javax.persistence.*;
      import java.util.*;
      
      @Entity
      @Table(name="order_items")
      public class OrderItem implements java.io.Serializable
      {
       private Long id;
       private Order order;
       private String product;
       private Double amount;
      
       @Id(generate=GeneratorType.AUTO)
       @Column(name="id",nullable=false,unique=true)
       public Long getId()
       {
       return id;
       }
       public void setId(Long id)
       {
       this.id = id;
       }
      
       @ManyToOne
       @JoinColumn (name="order_id", nullable=false)
       public Order getOrder()
       {
       return order;
       }
       public void setOrder(Order order)
       {
       this.order = order;
       }
      
       @Basic
       @Column(name="product",nullable=false)
       public String getProduct()
       {
       return product;
       }
       public void setProduct(String product)
       {
       this.product = product;
       }
      
       @Basic
       @Column(name="amount",nullable=false)
       public Double getAmount()
       {
       return amount;
       }
       public void setAmount(Double amount)
       {
       this.amount = amount;
       }
      }
      


      Remote interface:
      package cz.qds.ejb3test.ifc;
      
      import javax.ejb.*;
      import cz.qds.ejb3test.ebs.*;
      
      @Remote
      public interface CashRegister
      {
       public void saveNewPayment( Order order);
       public Order loadSavedPayment(Long id);
       public void changeSavedPayment( Order order);
      }
      


      Stateful Session Bean:
      package cz.qds.ejb3test.sbs;
      
      import cz.qds.ejb3test.ebs.*;
      import cz.qds.ejb3test.ifc.*;
      
      import javax.ejb.*;
      import javax.persistence.*;
      
      @Stateful
      public class CashRegisterBean implements CashRegister
      {
      @PersistenceContext // use @Inject in Preview releases
      private EntityManager manager;
      
       public Order loadSavedPayment(Long id)
       {
       Order o = manager.find(Order.class, id);
       return o;
       }
      
       public void saveNewPayment(Order order)
       {
       manager.persist(order);
       }
      
       public void changeSavedPayment(Order order)
       {
       manager.merge(order);
       }
      }
      


      Client:
      package cz.qds.ejb3test.textclient;
      
      import cz.qds.ejb3test.ebs.*;
      import cz.qds.ejb3test.ifc.*;
      import javax.naming.*;
      import java.util.*;
      
      public class TextClient
      {
       CashRegister cr = null;
      
       private void printSavedOrder(Long id, String info)
       {
       Order order = cr.loadSavedPayment(id);
       if (order!=null)
       {
       // print it
       System.out.println("Order:"+info);
       for (OrderItem oi: order.getItems())
       {
       System.out.println(oi.getProduct());
       }
       }
       }
      
       public TextClient()
       {
       try
       {
       String serverName = "localhost";
       Hashtable<String,String> ht = new Hashtable<String,String>();
       ht.put("java.naming.factory.initial","org.jnp.interfaces.NamingContextFactory");
       ht.put("java.naming.factory.url.pkgs","org.jboss.naming:org.jnp.interfaces");
       ht.put("java.naming.provider.url",serverName);
       InitialContext ctx = new InitialContext(ht);
       cr = (CashRegister) ctx.lookup(CashRegister.class.getName());
      
       { // create and persist order
       Order order = new Order();
       ArrayList<OrderItem> items = new ArrayList<OrderItem>();
       { //add item to order
       OrderItem item = new OrderItem();
       item.setOrder(order);
       item.setProduct("Apple");
       item.setAmount(1.0);
       items.add(item);
       }
       { //add item to order
       OrderItem item = new OrderItem();
       item.setOrder(order);
       item.setProduct("Strawberry");
       item.setAmount(1.0);
       items.add(item);
       }
       order.setItems(items);
       cr.saveNewPayment(order);
       }
      
       printSavedOrder(1L, "After persist");
      
       { // load, change items and save order
       Order order = cr.loadSavedPayment(1L);
       if (order!=null)
       {
       for (OrderItem oi: order.getItems())
       {
       oi.setProduct(oi.getProduct()+" Special!");
       }
       cr.changeSavedPayment(order);
       }
       }
      
       printSavedOrder(1L, "After change of items");
      
       { // load, remove first item and save order
       Order order = cr.loadSavedPayment(1L);
       if (order!=null)
       {
       int counter = 1;
       Iterator i = order.getItems().iterator();
       while (i.hasNext())
       {
       OrderItem oi = (OrderItem) i.next();
       if (counter==1)
       {
       i.remove();
       }
       counter++;
       }
       System.out.println("Order: Local copy after delete");
       for (OrderItem oi: order.getItems())
       {
       System.out.println(oi.getProduct());
       }
       cr.changeSavedPayment(order);
       }
       }
      
       printSavedOrder(1L, "Persistued copy after delete");
      
       }
       catch (javax.naming.NamingException e)
       {
       System.err.println("NamingException:"+e);
       }
       }
      
       public static void main(String[] args)
       {
       new TextClient();
       }
      }
      


      And result:
      Order:After persist
      Apple
      Strawberry

      Order:After change of items
      Apple Special!
      Strawberry Special!

      Order: Local copy after delete
      Strawberry Special!

      Order:Persistued copy after delete
      Apple Special! This must be deleted in persistence storage!
      Strawberry Special!

      Thanks,
      Leonell

        • 1. Re: Problem with removing item from collection in detached o
          bill.burke

          orphans are not cleaned up. AFAIK, this is the same behavior in EJB 2.1 CMP. Some want to add a switch for orphan deletion. email ejb3-feedback@sun.com and complain that you want orphan deletion.

          • 2. Re: Problem with removing item from collection in detached o
            leonell

            I don't mean that this is orphan. The orphan is child without parent, for example OrderItem without Order.

            One real example:
            Assume that here is some internet shop. Customer creates order, selects required OrderItems and then order saves (to persistent storage) - the same example as in tutorial.
            Until salesman commits this order customer can change it.
            And our customer wants remove one item - application will load the whole Order, customer will remove Item and application then will save updated Order to storage.
            But next time, when salesman and/or customer will load Order then removed item will be present and will be supplied by shop ;-)

            May be merge() is not way how to remove items from collection but then there must exist other way how to do it.

            Leonell

            • 3. Re: Problem with removing item from collection in detached o
              murtuza52

              I agree with Leonell as this can create more problems and lemme explain. If the OrderItem is made orphan (by this word i mean the OrderItem is still present in the storage but not linked to any Order). The movement on that particular report will not be correct.

              Murtuza

              • 4. Re: Problem with removing item from collection in detached o
                bill.burke

                the owning side is the ManyToOne side, so you have to do this:


                order.lineItems.remove(item);
                item.setOrder(null);

                Just lilke plain java.

                • 5. Re: Problem with removing item from collection in detached o
                  leonell

                  With detached entity Order and merge() this does not work.

                  With session bean's method is possible to remove item from collection:

                   public void removeItemFromSavedPayment(Order order, OrderItem item)
                   {
                   // now is order and item detached (remember, this method is called from client)
                   Order o = manager.find(Order.class, order.getId());
                   OrderItem oi = manager.find(OrderItem.class, item.getId());
                   // now is order and orderitem reattached...
                   // remove item from order
                   o.getItems().remove(oi);
                   oi.setOrder(null);
                   // update order
                   manager.merge(o);
                   // remove item
                   manager.remove(oi);
                   }
                  
                  


                  This is required way???

                  Leonell

                  • 6. Re: Problem with removing item from collection in detached o
                    epbernard

                    merge is not needed and is a no-op when you load an object in the same persistence context.
                    @org.hibernate.annotations.Cascade(CascadeType.DELETE_ORPHAN) should solve your problem, or maybe you did not commit your tx.

                    • 7. Re: Problem with removing item from collection in detached o
                      murtuza52

                      Hi Ephernard,

                      After much search on where i can find any resource or examples of @org.hibernate.annotations.Cascade(CascadeType.DELETE_ORPHAN) i could not locate any. Can you point me to right examples or tutorials where it demonstrates the use ot above mentioned tag.

                      The hibernate3.jar and hibernate-annotations.jar included the Ejb3 Preview 5 does not include such Cascade and the code will not compile.

                      Your help will be appreciated.

                      Thanks
                      Murtuza

                      • 8. Re: Problem with removing item from collection in detached o
                        kabirkhan

                        Upgrade to EJB 3 Beta 1

                        • 9. Re: Problem with removing item from collection in detached o
                          murtuza52

                          Yes, i test with EJB Beta and it seems that orphan still doesnot work. Here is the scenario
                          Category (1)-------------(M) Product

                          The infact even the adding new Product to the category also does not work untill i setProducts(products) again. This is weird as it was working find with preview 5. Here are the entity beans

                          Category

                          package com.kt.inventory.entity.bean;
                          
                          import org.hibernate.annotations.*;
                          import javax.persistence.Entity;
                          import javax.persistence.FetchType;
                          import javax.persistence.GeneratorType;
                          import javax.persistence.Id;
                          import javax.persistence.OneToMany;
                          import javax.persistence.Table;
                          import javax.persistence.Id;
                          import javax.persistence.CascadeType;
                          import javax.persistence.FetchType;
                          
                          import java.util.ArrayList;
                          import java.util.Collection;
                          
                          /**
                           * Comment
                           *
                           * @author <a href="mailto:murtuza@murtuza.net">Murtuza Vohra</a>
                           * @version $Revision: 1.0 $
                           */
                          @Entity
                          @Table(name = "category")
                          public class Category implements java.io.Serializable {
                           private long catNo;
                          
                           private String catName;
                          
                           private long parent;
                          
                           private String description;
                          
                           private String flex1;
                          
                           private String flex2;
                          
                           private String flex3;
                          
                           private Collection<Product> products;
                          
                           public Category() {
                           }
                          
                           public Category(String name, long parent, String description, String flex1, String flex2, String flex3) {
                           super();
                           // TODO Auto-generated constructor stub
                           catName = name;
                           this.parent = parent;
                           this.description = description;
                           this.flex1 = flex1;
                           this.flex2 = flex2;
                           this.flex3 = flex3;
                           }
                          
                           public Category(long no, String name, long parent) {
                           super();
                           // TODO Auto-generated constructor stub
                           catNo = no;
                           catName = name;
                           this.parent = parent;
                           }
                          
                           @Id(generate = GeneratorType.AUTO)
                           public long getCatNo() {
                           return catNo;
                           }
                          
                           public void setCatNo(long catNo) {
                           this.catNo = catNo;
                           }
                          
                           public String getDescription() {
                           return description;
                           }
                          
                           public void setDescription(String description) {
                           this.description = description;
                           }
                          
                           public String getFlex1() {
                           return flex1;
                           }
                          
                           public void setFlex1(String flex1) {
                           this.flex1 = flex1;
                           }
                          
                           public String getFlex2() {
                           return flex2;
                           }
                          
                           public void setFlex2(String flex2) {
                           this.flex2 = flex2;
                           }
                          
                           public String getFlex3() {
                           return flex3;
                           }
                          
                           public void setFlex3(String flex3) {
                           this.flex3 = flex3;
                           }
                          
                           public String getCatName() {
                           return catName;
                           }
                          
                           public void setCatName(String catName) {
                           this.catName = catName;
                           }
                          
                           public long getParent() {
                           return parent;
                           }
                          
                           public void setParent(long parent) {
                           this.parent = parent;
                           }
                          
                           @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "category")
                           @org.hibernate.annotations.Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
                           public Collection<Product> getProducts() {
                           return products;
                           }
                          
                           public void setProducts(Collection<Product> products) {
                           this.products = products;
                           }
                          
                          }
                          


                          Product Entity
                          package com.kt.inventory.entity.bean;
                          
                          import javax.persistence.CascadeType;
                          import javax.persistence.Entity;
                          import javax.persistence.FetchType;
                          import javax.persistence.GeneratorType;
                          import javax.persistence.Id;
                          import javax.persistence.JoinColumn;
                          import javax.persistence.ManyToOne;
                          import javax.persistence.OneToMany;
                          import javax.persistence.Table;
                          import javax.persistence.Id;
                          import javax.persistence.CascadeType;
                          import javax.persistence.FetchType;
                          
                          import java.io.Serializable;
                          import java.util.ArrayList;
                          import java.util.Collection;
                          
                          /**
                           * Comment
                           *
                           * @author <a href="mailto:murtuza@murtuza.net">Murtuza Vohra</a>
                           * @version $Revision: 1.0 $
                           */
                          @Entity
                          @Table(name = "product")
                          public class Product implements Serializable {
                          
                           private String country;
                          
                           private String dcountry;
                          
                           private String description;
                          
                           private String flex1;
                          
                           private String flex2;
                          
                           private String flex3;
                          
                           private boolean isCancelled;
                          
                           private String partNumber;
                          
                           private String productName;
                          
                           private long productNo;
                          
                           private double reorderLevel;
                          
                           private Category category;
                          
                           public Product() {
                           super();
                           // TODO Auto-generated constructor stub
                           }
                          
                           public Product(String name, String number, String country, String description, Category category) {
                           super();
                           // TODO Auto-generated constructor stub
                           this.country = country;
                           this.description = description;
                           partNumber = number;
                           productName = name;
                           this.category = category;
                           }
                          
                           public Product(String name, String number, String country, String dcountry, String description, String flex1, String flex2, String flex3, boolean cancelled, double level, Category category) {
                           super();
                           // TODO Auto-generated constructor stub
                           this.country = country;
                           this.dcountry = dcountry;
                           this.description = description;
                           this.flex1 = flex1;
                           this.flex2 = flex2;
                           this.flex3 = flex3;
                           isCancelled = cancelled;
                           partNumber = number;
                           productName = name;
                           reorderLevel = level;
                           this.category = category;
                           }
                          
                           public String getCountry() {
                           return country;
                           }
                          
                           public void setCountry(String country) {
                           this.country = country;
                           }
                          
                           public String getDcountry() {
                           return dcountry;
                           }
                          
                           public void setDcountry(String dcountry) {
                           this.dcountry = dcountry;
                           }
                          
                           public String getDescription() {
                           return description;
                           }
                          
                           public void setDescription(String description) {
                           this.description = description;
                           }
                          
                           public String getFlex1() {
                           return flex1;
                           }
                          
                           public void setFlex1(String flex1) {
                           this.flex1 = flex1;
                           }
                          
                           public String getFlex2() {
                           return flex2;
                           }
                          
                           public void setFlex2(String flex2) {
                           this.flex2 = flex2;
                           }
                          
                           public String getFlex3() {
                           return flex3;
                           }
                          
                           public void setFlex3(String flex3) {
                           this.flex3 = flex3;
                           }
                          
                           public boolean isCancelled() {
                           return isCancelled;
                           }
                          
                           public void setCancelled(boolean isCancelled) {
                           this.isCancelled = isCancelled;
                           }
                          
                           public String getPartNumber() {
                           return partNumber;
                           }
                          
                           public void setPartNumber(String partNumber) {
                           this.partNumber = partNumber;
                           }
                          
                           public String getProductName() {
                           return productName;
                           }
                          
                           public void setProductName(String productName) {
                           this.productName = productName;
                           }
                          
                           @Id(generate = GeneratorType.AUTO)
                           public long getProductNo() {
                           return productNo;
                           }
                          
                           public void setProductNo(long productNo) {
                           this.productNo = productNo;
                           }
                          
                           public double getReorderLevel() {
                           return reorderLevel;
                           }
                          
                           public void setReorderLevel(double reorderLevel) {
                           this.reorderLevel = reorderLevel;
                           }
                          
                           @ManyToOne
                           @JoinColumn(name = "catNo")
                           public Category getCategory() {
                           return category;
                           }
                          
                           public void setCategory(Category category) {
                           this.category = category;
                           }
                          }
                          
                          



                          The InventoryManager session bean

                          package com.kt.inventory.session.bean;
                          
                          import java.util.*;
                          import javax.ejb.Remote;
                          import javax.ejb.Stateless;
                          import javax.persistence.EntityManager;
                          import javax.persistence.PersistenceContext;
                          
                          
                          import com.kt.inventory.entity.bean.*;
                          
                          @Stateless
                          @Remote(InventoryManager.class)
                          public class InventoryManagerBean implements InventoryManager{
                          
                           private @PersistenceContext(unitName="ktmanager") EntityManager manager;
                          
                           public Category findCategoryById(long id) throws Exception{
                           return manager.find(Category.class, new Long(id));
                           }
                          
                           public Collection<Category> findSubCategory(long parent) throws Exception{
                           return manager.createQuery("from Category c where c.parent = :parent").setParameter("parent", parent).getResultList();
                           }
                          
                           public Collection<Category> findCategoryByKeyword(String keywords) throws Exception{
                           return manager.createQuery("SELECT OBJECT(c) FROM Category AS c WHERE "+
                           "c.catName LIKE '%"+keywords+"%' OR " +
                           "c.description LIKE '%"+keywords+"%' OR " +
                           "c.flex1 LIKE '%"+keywords+"%' OR "+
                           "c.flex2 LIKE '%"+keywords+"%' OR "+
                           "c.flex3 LIKE '%"+keywords+"%'").getResultList();
                           }
                          
                           public Product findProductById(long id) throws Exception{
                           return manager.find(Product.class, new Long(id));
                           }
                          
                           public Collection<Product> findProductByCategory(long catNo) throws Exception{
                           return manager.find(Category.class, new Long(catNo)).getProducts();
                           }
                          
                           public Collection<Product> findProductByKeyword(String keywords) throws Exception {
                           return manager.createQuery("SELECT OBJECT(p) FROM Product as p WHERE "+
                           "p.productName LIKE '%"+keywords+"%' OR " +
                           "p.description LIKE '%"+keywords+"%' OR " +
                           "p.country LIKE '%"+keywords+"%' OR " +
                           "p.dcountry LIKE '%"+keywords+"%' OR " +
                           "p.partNumber LIKE '%"+keywords+"%' OR " +
                           "p.flex1 LIKE '%"+keywords+"%' OR "+
                           "p.flex2 LIKE '%"+keywords+"%' OR "+
                           "p.flex3 LIKE '%"+keywords+"%'").getResultList();
                           }
                          
                           //public void addCategory(long catno, String name, long parent, String description) throws Exception
                           public long addCategory(Category category) throws Exception
                           {
                           /*Category category = new Category();
                           category.setCatNo(catno);
                           category.setCatName(name);
                           category.setParent(parent);
                           category.setDescription(description);*/
                           if(category != null)
                           {
                           manager.persist(category);
                           //category = manager.find(Category.class, new Long(category.getCatNo()));
                           }
                           return category.getCatNo();
                           }
                          
                           public void udpateCategory(Category category) throws Exception
                           {
                           if(category!=null)
                           {
                           System.out.println("Products: " + category.getProducts().size());
                          
                           manager.merge(category);
                           manager.find(Category.class, new Long(category.getCatNo()));
                           }
                           }
                          
                          }
                          
                          



                          and the test client
                          /*
                           * JBoss, the OpenSource J2EE webOS
                           *
                           * Distributable under LGPL license.
                           * See terms of license at gnu.org.
                           */
                          package com.kt.test.order;
                          
                          import com.kt.order.entity.bean.*;
                          import com.kt.order.session.bean.*;
                          import com.kt.inventory.entity.bean.*;
                          import com.kt.inventory.session.bean.*;
                          
                          import javax.naming.InitialContext;
                          import javax.naming.NamingException;
                          
                          import java.util.*;
                          
                          /**
                           * Comment
                           *
                           * @author <a href="mailto:bill@jboss.org">Bill Burke</a>
                           * @version $Revision: 1.1.6.3 $
                           */
                          public class TestClient
                          {
                           public static void main(String[] args)
                           {
                           try {
                           //String serverName = "localhost";
                           //Hashtable<String,String> ht = new Hashtable<String,String>();
                           //ht.put("java.naming.factory.initial","org.jnp.interfaces.NamingContextFactory");
                           //ht.put("java.naming.factory.url.pkgs","org.jboss.naming:org.jnp.interfaces");
                           //ht.put("java.naming.provider.url",serverName);
                           SecurityManager security = System.getSecurityManager();
                           InitialContext ctx = new InitialContext();
                          
                           //testAddCatAndSubCat(ctx);
                           //testAddProduct(ctx);
                           //testUpdateProduct(ctx);
                           testAddDeleteProduct(ctx);
                           } catch (NamingException e) {
                           // TODO Auto-generated catch block
                           e.printStackTrace();
                           }
                           }
                          
                           public static void testAddDeleteProduct(InitialContext ctx)
                           {
                           try {
                           InventoryManager im = (InventoryManager)ctx.lookup(InventoryManager.class.getName());
                           Collection<Category> list = im.findCategoryByKeyword("hinges");
                           if(list==null)
                           return;
                           Category cat = new LinkedList<Category>(list).get(0);
                           System.out.println("Found Category: " + cat.getCatName() + " catNo: " + cat.getCatNo());
                          
                           LinkedList<Product> products = new LinkedList<Product>(cat.getProducts());
                           System.out.println("Found products: " + products.size());
                           Product prod = products.remove(0);
                           prod.setCategory(null);
                           System.out.println("Removed: " + prod.getProductName() + " " + prod.getPartNumber() +" NEW SIZE: " + products.size());
                          
                           products.get(1).setFlex1("UK Product");
                          
                           products.add(new Product("Steel Hinges", "4\"", "India", "", cat));
                           System.out.println("Updated products: " + products.size());
                           cat.setProducts(products);
                           im.udpateCategory(cat);
                           System.out.println("Category updated");
                           } catch (NamingException e) {
                           // TODO Auto-generated catch block
                           e.printStackTrace();
                           } catch (Exception e) {
                           // TODO Auto-generated catch block
                           e.printStackTrace();
                           }
                           }
                          
                          }
                          


                          The client side result show correctly that product entity was removed
                          Found Category: Hinges catNo: 7
                          Found products: 3
                          Removed: Steel Hinges 2" NEW SIZE: 2
                          Updated products: 3
                          Category updated

                          But the database had new Products added, modified products updated but removed product not deleted from database.

                          Strangely if code "cat.setProducts(products);" is commented in the client program, then database will show the removed entity has catNo NULL but not removed. But with above line commented, the new entity is not added or modified entity is not updated. Is this bug as there are no error message thrown on server.

                          I'll appreciate someone help. I want the dynamism to add, remove or update entity in OneToMany relation.

                          Thanks
                          Murtuza

                          • 10. Re: Problem with removing item from collection in detached o
                            murtuza52

                            Yes, i test with EJB Beta and it seems that orphan still doesnot work. Here is the scenario
                            Category (1)-------------(M) Product

                            The infact even the adding new Product to the category also does not work untill i setProducts(products) again. This is weird as it was working find with preview 5. Here are the entity beans

                            Category

                            package com.kt.inventory.entity.bean;
                            
                            import org.hibernate.annotations.*;
                            import javax.persistence.Entity;
                            import javax.persistence.FetchType;
                            import javax.persistence.GeneratorType;
                            import javax.persistence.Id;
                            import javax.persistence.OneToMany;
                            import javax.persistence.Table;
                            import javax.persistence.Id;
                            import javax.persistence.CascadeType;
                            import javax.persistence.FetchType;
                            
                            import java.util.ArrayList;
                            import java.util.Collection;
                            
                            /**
                             * Comment
                             *
                             * @author <a href="mailto:murtuza@murtuza.net">Murtuza Vohra</a>
                             * @version $Revision: 1.0 $
                             */
                            @Entity
                            @Table(name = "category")
                            public class Category implements java.io.Serializable {
                             private long catNo;
                            
                             private String catName;
                            
                             private long parent;
                            
                             private String description;
                            
                             private String flex1;
                            
                             private String flex2;
                            
                             private String flex3;
                            
                             private Collection<Product> products;
                            
                             public Category() {
                             }
                            
                             public Category(String name, long parent, String description, String flex1, String flex2, String flex3) {
                             super();
                             // TODO Auto-generated constructor stub
                             catName = name;
                             this.parent = parent;
                             this.description = description;
                             this.flex1 = flex1;
                             this.flex2 = flex2;
                             this.flex3 = flex3;
                             }
                            
                             public Category(long no, String name, long parent) {
                             super();
                             // TODO Auto-generated constructor stub
                             catNo = no;
                             catName = name;
                             this.parent = parent;
                             }
                            
                             @Id(generate = GeneratorType.AUTO)
                             public long getCatNo() {
                             return catNo;
                             }
                            
                             public void setCatNo(long catNo) {
                             this.catNo = catNo;
                             }
                            
                             public String getDescription() {
                             return description;
                             }
                            
                             public void setDescription(String description) {
                             this.description = description;
                             }
                            
                             public String getFlex1() {
                             return flex1;
                             }
                            
                             public void setFlex1(String flex1) {
                             this.flex1 = flex1;
                             }
                            
                             public String getFlex2() {
                             return flex2;
                             }
                            
                             public void setFlex2(String flex2) {
                             this.flex2 = flex2;
                             }
                            
                             public String getFlex3() {
                             return flex3;
                             }
                            
                             public void setFlex3(String flex3) {
                             this.flex3 = flex3;
                             }
                            
                             public String getCatName() {
                             return catName;
                             }
                            
                             public void setCatName(String catName) {
                             this.catName = catName;
                             }
                            
                             public long getParent() {
                             return parent;
                             }
                            
                             public void setParent(long parent) {
                             this.parent = parent;
                             }
                            
                             @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "category")
                             @org.hibernate.annotations.Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
                             public Collection<Product> getProducts() {
                             return products;
                             }
                            
                             public void setProducts(Collection<Product> products) {
                             this.products = products;
                             }
                            
                            }
                            


                            Product Entity
                            package com.kt.inventory.entity.bean;
                            
                            import javax.persistence.CascadeType;
                            import javax.persistence.Entity;
                            import javax.persistence.FetchType;
                            import javax.persistence.GeneratorType;
                            import javax.persistence.Id;
                            import javax.persistence.JoinColumn;
                            import javax.persistence.ManyToOne;
                            import javax.persistence.OneToMany;
                            import javax.persistence.Table;
                            import javax.persistence.Id;
                            import javax.persistence.CascadeType;
                            import javax.persistence.FetchType;
                            
                            import java.io.Serializable;
                            import java.util.ArrayList;
                            import java.util.Collection;
                            
                            /**
                             * Comment
                             *
                             * @author <a href="mailto:murtuza@murtuza.net">Murtuza Vohra</a>
                             * @version $Revision: 1.0 $
                             */
                            @Entity
                            @Table(name = "product")
                            public class Product implements Serializable {
                            
                             private String country;
                            
                             private String dcountry;
                            
                             private String description;
                            
                             private String flex1;
                            
                             private String flex2;
                            
                             private String flex3;
                            
                             private boolean isCancelled;
                            
                             private String partNumber;
                            
                             private String productName;
                            
                             private long productNo;
                            
                             private double reorderLevel;
                            
                             private Category category;
                            
                             public Product() {
                             super();
                             // TODO Auto-generated constructor stub
                             }
                            
                             public Product(String name, String number, String country, String description, Category category) {
                             super();
                             // TODO Auto-generated constructor stub
                             this.country = country;
                             this.description = description;
                             partNumber = number;
                             productName = name;
                             this.category = category;
                             }
                            
                             public Product(String name, String number, String country, String dcountry, String description, String flex1, String flex2, String flex3, boolean cancelled, double level, Category category) {
                             super();
                             // TODO Auto-generated constructor stub
                             this.country = country;
                             this.dcountry = dcountry;
                             this.description = description;
                             this.flex1 = flex1;
                             this.flex2 = flex2;
                             this.flex3 = flex3;
                             isCancelled = cancelled;
                             partNumber = number;
                             productName = name;
                             reorderLevel = level;
                             this.category = category;
                             }
                            
                             public String getCountry() {
                             return country;
                             }
                            
                             public void setCountry(String country) {
                             this.country = country;
                             }
                            
                             public String getDcountry() {
                             return dcountry;
                             }
                            
                             public void setDcountry(String dcountry) {
                             this.dcountry = dcountry;
                             }
                            
                             public String getDescription() {
                             return description;
                             }
                            
                             public void setDescription(String description) {
                             this.description = description;
                             }
                            
                             public String getFlex1() {
                             return flex1;
                             }
                            
                             public void setFlex1(String flex1) {
                             this.flex1 = flex1;
                             }
                            
                             public String getFlex2() {
                             return flex2;
                             }
                            
                             public void setFlex2(String flex2) {
                             this.flex2 = flex2;
                             }
                            
                             public String getFlex3() {
                             return flex3;
                             }
                            
                             public void setFlex3(String flex3) {
                             this.flex3 = flex3;
                             }
                            
                             public boolean isCancelled() {
                             return isCancelled;
                             }
                            
                             public void setCancelled(boolean isCancelled) {
                             this.isCancelled = isCancelled;
                             }
                            
                             public String getPartNumber() {
                             return partNumber;
                             }
                            
                             public void setPartNumber(String partNumber) {
                             this.partNumber = partNumber;
                             }
                            
                             public String getProductName() {
                             return productName;
                             }
                            
                             public void setProductName(String productName) {
                             this.productName = productName;
                             }
                            
                             @Id(generate = GeneratorType.AUTO)
                             public long getProductNo() {
                             return productNo;
                             }
                            
                             public void setProductNo(long productNo) {
                             this.productNo = productNo;
                             }
                            
                             public double getReorderLevel() {
                             return reorderLevel;
                             }
                            
                             public void setReorderLevel(double reorderLevel) {
                             this.reorderLevel = reorderLevel;
                             }
                            
                             @ManyToOne
                             @JoinColumn(name = "catNo")
                             public Category getCategory() {
                             return category;
                             }
                            
                             public void setCategory(Category category) {
                             this.category = category;
                             }
                            }
                            
                            



                            The InventoryManager session bean

                            package com.kt.inventory.session.bean;
                            
                            import java.util.*;
                            import javax.ejb.Remote;
                            import javax.ejb.Stateless;
                            import javax.persistence.EntityManager;
                            import javax.persistence.PersistenceContext;
                            
                            
                            import com.kt.inventory.entity.bean.*;
                            
                            @Stateless
                            @Remote(InventoryManager.class)
                            public class InventoryManagerBean implements InventoryManager{
                            
                             private @PersistenceContext(unitName="ktmanager") EntityManager manager;
                            
                             public Category findCategoryById(long id) throws Exception{
                             return manager.find(Category.class, new Long(id));
                             }
                            
                             public Collection<Category> findSubCategory(long parent) throws Exception{
                             return manager.createQuery("from Category c where c.parent = :parent").setParameter("parent", parent).getResultList();
                             }
                            
                             public Collection<Category> findCategoryByKeyword(String keywords) throws Exception{
                             return manager.createQuery("SELECT OBJECT(c) FROM Category AS c WHERE "+
                             "c.catName LIKE '%"+keywords+"%' OR " +
                             "c.description LIKE '%"+keywords+"%' OR " +
                             "c.flex1 LIKE '%"+keywords+"%' OR "+
                             "c.flex2 LIKE '%"+keywords+"%' OR "+
                             "c.flex3 LIKE '%"+keywords+"%'").getResultList();
                             }
                            
                             public Product findProductById(long id) throws Exception{
                             return manager.find(Product.class, new Long(id));
                             }
                            
                             public Collection<Product> findProductByCategory(long catNo) throws Exception{
                             return manager.find(Category.class, new Long(catNo)).getProducts();
                             }
                            
                             public Collection<Product> findProductByKeyword(String keywords) throws Exception {
                             return manager.createQuery("SELECT OBJECT(p) FROM Product as p WHERE "+
                             "p.productName LIKE '%"+keywords+"%' OR " +
                             "p.description LIKE '%"+keywords+"%' OR " +
                             "p.country LIKE '%"+keywords+"%' OR " +
                             "p.dcountry LIKE '%"+keywords+"%' OR " +
                             "p.partNumber LIKE '%"+keywords+"%' OR " +
                             "p.flex1 LIKE '%"+keywords+"%' OR "+
                             "p.flex2 LIKE '%"+keywords+"%' OR "+
                             "p.flex3 LIKE '%"+keywords+"%'").getResultList();
                             }
                            
                             //public void addCategory(long catno, String name, long parent, String description) throws Exception
                             public long addCategory(Category category) throws Exception
                             {
                             /*Category category = new Category();
                             category.setCatNo(catno);
                             category.setCatName(name);
                             category.setParent(parent);
                             category.setDescription(description);*/
                             if(category != null)
                             {
                             manager.persist(category);
                             //category = manager.find(Category.class, new Long(category.getCatNo()));
                             }
                             return category.getCatNo();
                             }
                            
                             public void udpateCategory(Category category) throws Exception
                             {
                             if(category!=null)
                             {
                             System.out.println("Products: " + category.getProducts().size());
                            
                             manager.merge(category);
                             manager.find(Category.class, new Long(category.getCatNo()));
                             }
                             }
                            
                            }
                            
                            



                            and the test client
                            /*
                             * JBoss, the OpenSource J2EE webOS
                             *
                             * Distributable under LGPL license.
                             * See terms of license at gnu.org.
                             */
                            package com.kt.test.order;
                            
                            import com.kt.order.entity.bean.*;
                            import com.kt.order.session.bean.*;
                            import com.kt.inventory.entity.bean.*;
                            import com.kt.inventory.session.bean.*;
                            
                            import javax.naming.InitialContext;
                            import javax.naming.NamingException;
                            
                            import java.util.*;
                            
                            /**
                             * Comment
                             *
                             * @author <a href="mailto:bill@jboss.org">Bill Burke</a>
                             * @version $Revision: 1.1.6.3 $
                             */
                            public class TestClient
                            {
                             public static void main(String[] args)
                             {
                             try {
                             //String serverName = "localhost";
                             //Hashtable<String,String> ht = new Hashtable<String,String>();
                             //ht.put("java.naming.factory.initial","org.jnp.interfaces.NamingContextFactory");
                             //ht.put("java.naming.factory.url.pkgs","org.jboss.naming:org.jnp.interfaces");
                             //ht.put("java.naming.provider.url",serverName);
                             SecurityManager security = System.getSecurityManager();
                             InitialContext ctx = new InitialContext();
                            
                             //testAddCatAndSubCat(ctx);
                             //testAddProduct(ctx);
                             //testUpdateProduct(ctx);
                             testAddDeleteProduct(ctx);
                             } catch (NamingException e) {
                             // TODO Auto-generated catch block
                             e.printStackTrace();
                             }
                             }
                            
                             public static void testAddDeleteProduct(InitialContext ctx)
                             {
                             try {
                             InventoryManager im = (InventoryManager)ctx.lookup(InventoryManager.class.getName());
                             Collection<Category> list = im.findCategoryByKeyword("hinges");
                             if(list==null)
                             return;
                             Category cat = new LinkedList<Category>(list).get(0);
                             System.out.println("Found Category: " + cat.getCatName() + " catNo: " + cat.getCatNo());
                            
                             LinkedList<Product> products = new LinkedList<Product>(cat.getProducts());
                             System.out.println("Found products: " + products.size());
                             Product prod = products.remove(0);
                             prod.setCategory(null);
                             System.out.println("Removed: " + prod.getProductName() + " " + prod.getPartNumber() +" NEW SIZE: " + products.size());
                            
                             products.get(1).setFlex1("UK Product");
                            
                             products.add(new Product("Steel Hinges", "4\"", "India", "", cat));
                             System.out.println("Updated products: " + products.size());
                             cat.setProducts(products);
                             im.udpateCategory(cat);
                             System.out.println("Category updated");
                             } catch (NamingException e) {
                             // TODO Auto-generated catch block
                             e.printStackTrace();
                             } catch (Exception e) {
                             // TODO Auto-generated catch block
                             e.printStackTrace();
                             }
                             }
                            
                            }
                            


                            The client side result show correctly that product entity was removed
                            Found Category: Hinges catNo: 7
                            Found products: 3
                            Removed: Steel Hinges 2" NEW SIZE: 2
                            Updated products: 3
                            Category updated

                            But the database had new Products added, modified products updated but removed product not deleted from database.

                            Strangely if code "cat.setProducts(products);" is commented in the client program, then database will show the removed entity has catNo NULL but not removed. But with above line commented, the new entity is not added or modified entity is not updated. Is this bug as there are no error message thrown on server.

                            I'll appreciate someone help. I want the dynamism to add, remove or update entity in OneToMany relation.

                            Thanks
                            Murtuza

                            • 11. Re: Problem with removing item from collection in detached o
                              epbernard

                              I must admit I don't have the energy to read your 9-pages code. Have a look at the Hibernate annotations unit test suite, it shows an example of DELETE_ORPHAN use.

                              • 12. Re: Problem with removing item from collection in detached o
                                murtuza52

                                HI

                                I looked in hibernate website for annocation unit test suite but could not find any. The annotation documentaiton does not show proper usage. If you can write me the url where i can find unit test suite documentation or tell me whether the usage below is correct.

                                @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "category")
                                 @org.hibernate.annotations.Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
                                 public Collection<Product> getProducts() {
                                 return products;
                                 }
                                


                                The session bean will use manage.merge(product).

                                Thanks
                                Murtuza