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.
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:
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.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).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.