Question

I have this simple situation:

@Entity
public class Customer{

    @ManyToMany(fetch=FetchType.EAGER)
    @Cascade(CascadeType.SAVE_UPDATE)
    private List<Product> products=new ArrayList<Product>();

}

@Entity
public class Produtc{

    @ManyToOne
    @Cascade(CascadeType.SAVE_UPDATE)
    private Category category;
}

@Entity
public class Category{

    private String name;
}

Routine to insert a new customer:

Customer customer=new Customer();
//customer.set data
Product p=dao.getProductBySomeUserInput...
if(p==null){
   p=new Product();
   //p.set data
}
customer.addProduct(p);
dao.save(customer);  //this row throw NonUniqueObjectException on Category class

How can i resolve this issue? I think the problem is related to CascadeType.SAVE_UPDATE, but i need it... Thanks all.


UPDATE

I found the problem and this is how to replicate it:

Customer c=new Customer();
// Load two products by id
Product p=dao.get(Product.class, 2);
Product p1=dao.get(Product.class, 3);
c.addProduct(p);
c.addProduct(p1);
// This try to update products, and category of products becouse CascadeType is SAVE_UDPATE
dao.save(c);

So, if p and p 1 has different categories there is no probs, but if p and p1 has same category i have NonUniqueObjectException on Category becouse same category is in session and hibernate try to save_update its.

I need CascadeType.SAVE_UPDATE in both Product and Category entities, so how can i resolve this issue? Thanks.

Was it helpful?

Solution

The problem is probably due to the bad transaction management. All your code should be done in a single transaction, rather than having a transaction for each DAO call. The service layer should be the one which demarcates transaction, and not the DAO layer.

I can imagine what Hibernate does:

  1. You call dao.get(Product.class, 2). This returns a detached product pointing to the category with ID 67 (for example). The result is detached because the transaction ends when the call to the DAO ends.
  2. You call dao.get(Product.class, 3). This returns a detached product pointing to the category with ID 67 (for example). But since the call runs in another transaction, you get a second, different, Category instance (with the same ID as the first one).
  3. You call saveOrUpdate on the product. This cascades to the category, and Hibernate must thus attach category1 and category2 to the session. Since they both have the same ID, one of the cascade may not be done.

If you use a single transaction to get both products, they will both have the same category instance, and the problem will not occur. Calling merge instead of saveOrUpdate should also work: Hibernate will copy the state of both categories to a third, attached one. But the right thing to do is to use a transaction involving both calls to the DAO.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top