0

I'm trying to learn and understand JPA, and just have a couple of questions regarding deleting a parent and its children in one go. I'm using OpenJPA and EJB3. I have two entities, a Category and a Product. A Category contains many products and a product has a reference to its parent category. The category's list of products is set to cascade.

//Category

    @Entity @NamedQueries({@NamedQuery(name = "Category.getCategoryByName", query = "SELECT c FROM Category c WHERE c.name = :name"),@NamedQuery(name = "Category.getCategoryByCategoryId", query = "SELECT c FROM Category c WHERE c.categoryid = :categoryid"), @NamedQuery(name = "Category.getAllCategories", query = "SELECT c FROM Category c left join fetch c.products")}) 

    public class Category implements Serializable {     
        private static final long serialVersionUID = 1L;

        @Id     
        @GeneratedValue(strategy=IDENTITY)  
        private Integer categoryid;         
        private String name;

        //bi-directional many-to-one association to Product     
        @OneToMany(cascade={CascadeType.ALL}, orphanRemoval = true, 
                   fetch = EAGER, mappedBy="category")  
        private List<Product> products;

    }

//Product

    @Entity
    @NamedQueries({@NamedQuery(name = "Product.getProductsByCategory", 
    query = "SELECT p.code, p.description, p.name, p.productid, p.weight FROM Product p    WHERE p.category.categoryid = :category_categoryid"),
    @NamedQuery(name = "Product.getProductByName", query = "SELECT p FROM Product p WHERE p.name = :name"),
    @NamedQuery(name = "Product.getProductByCode", query = "SELECT p FROM Product p WHERE p.code = :code"),
    @NamedQuery(name = "Product.getProductByProductId", query = "SELECT p FROM Product p WHERE p.productid = :productid"),
    @NamedQuery(name = "Product.getAllProducts", query = "SELECT p FROM Product p")})
     public class Product implements Serializable {
          private static final long serialVersionUID = 1L;

      @Id
      @GeneratedValue(strategy=IDENTITY)
      private Integer productid;
      private String code;
      private String description;
      private String name;
      private Double weight;

      //bi-directional many-to-one association to Category
      @ManyToOne(optional = false)
      @JoinColumn(name="CATEGORYID")
      private Category category;
    }
}

    // The EJB 

        @Stateless
        @LocalBean
        public class ShopManagerBean implements Serializable {
        @PersistenceContext(unitName = "TestEJBProject2", type =  PersistenceContextType.TRANSACTION)
        private EntityManager entityManager;



        @TransactionAttribute(TransactionAttributeType.REQUIRED)
        public void deleteCategory(Category category)
                throws TestApplicationException {

            try {
                Category actualCat = entityManager.find(Category.class,
                        category.getCategoryid());
                List<Product> products = actualCat.getProducts();
                if (products != null) {
                    Iterator<Product> it = products.iterator();
                    while (it.hasNext()) {
                        Product p = it.next();
                        it.remove();
                        entityManager.remove(p);
                    }
                }

                entityManager.refresh(actualCat);
                entityManager.remove(actualCat);

            } catch (Exception e) {
                e.printStackTrace();
                throw new TestApplicationException("Error creating new Product", e);
            }
        }
    }

If I use the following code in the deleteCategory method the EJB then I cannot delete the parent and children as I get an Optimistic Locking exception (An optimistic lock violation was detected when flushing object instance "entity.Product-101" to the data store. This indicates that the object was concurrently modified in another transaction.) - complaining about flushing the product child to the data store

 Category actualCat = entityManager.find(Category.class, category.getCategoryid());

  if (products != null) {
       actualCat.getProducts().clear();
   }
   entityManager.remove(actualCat);

However, if I use the following code in the deleteCategory method then I can delete the parent and children...but only if I call entityManager.refresh(actualCat) after removing the children and before removing the parent (otherwise I get an optimistic locking exception). Could somebody please explain to me why this is the case and also what the correct/best way of doing a cascading delete with OpenJPA V2 would be?

Category actualCat = entityManager.find(Category.class, category.getCategoryid());
List<Product> products = actualCat.getProducts();
if (products != null) {
    Iterator<Product> it = products.iterator();
    while (it.hasNext()) {
        Product p = it.next();
        it.remove();
        entityManager.remove(p);
    }
}
entityManager.refresh(actualCat);
entityManager.remove(actualCat);

Thanks in advance for your help

Fais


Addition

Here is the db creation script:

--

CREATE SCHEMA "DB2ADMIN";

CREATE TABLE "DB2ADMIN"."CATEGORY" ( "CATEGORYID" INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY ( START WITH 1 INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 NO CYCLE CACHE 20), "NAME" VARCHAR(50) NOT NULL ) DATA CAPTURE NONE;

CREATE TABLE "DB2ADMIN"."PRODUCT" ( "PRODUCTID" INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY ( START WITH 1 INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 NO CYCLE CACHE 20), "CODE" CHAR(15) NOT NULL, "NAME" VARCHAR(50) NOT NULL, "DESCRIPTION" VARCHAR(200) NOT NULL, "WEIGHT" FLOAT(53) NOT NULL, "CATEGORYID" INTEGER NOT NULL ) DATA CAPTURE NONE;

ALTER TABLE "DB2ADMIN"."CATEGORY" ADD CONSTRAINT "CATEGORY_PK" PRIMARY KEY ("CATEGORYID");

ALTER TABLE "DB2ADMIN"."PRODUCT" ADD CONSTRAINT "PRODUCT_PK" PRIMARY KEY ("PRODUCTID");

ALTER TABLE "DB2ADMIN"."PRODUCT" ADD CONSTRAINT "PRODUCT_CATEGORY_FK" FOREIGN KEY ("CATEGORYID") REFERENCES "DB2ADMIN"."CATEGORY" ("CATEGORYID") ON DELETE CASCADE;

zargarf
  • 633
  • 6
  • 18
  • Here is the db creation script too: – zargarf May 24 '13 at 09:28
  • Have you tried refreshing the Category entity right after your find call? I don't think your attempt to remove referenced Product entities directly should have any effect - refreshing Category would bring them back into the EM again referenced to Category. Check that there aren't any changes to the Category version number when you call find and when you call flush, as the error might be correct and your EntityManager has a stale version of it. – Chris May 27 '13 at 13:34

0 Answers0