0

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.

blow
  • 12,811
  • 24
  • 75
  • 112
  • Possible duplicate of http://stackoverflow.com/questions/3543716/hibernate-and-nonuniqueobjectexception – Beau Grantham Dec 06 '11 at 22:58
  • are you by any chance setting id on the customer object? does an object with the same id already exist in hibernate's session? – aishwarya Dec 07 '11 at 00:07
  • There is no save method in EntityManager. Which method do you use to persist the customer? Does this code run inside a unique transaction? – JB Nizet Dec 07 '11 at 18:31
  • @JB Nizet: sorry, entityManager is my piece of code, however, dao.get and dao.save each of both use a transaction(i manage transaction with spring marking them with @Transactional), so no, this code does not run in a single transaction. – blow Dec 07 '11 at 19:30
  • I'm using a single entity manager for every entities in my projects, so method like save, delete, findById, findAll, each of them are managed with a transaction. – blow Dec 07 '11 at 19:32

1 Answers1

2

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.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255