hibernate NonUniqueObjectException при сохранении обновления - PullRequest
0 голосов
/ 07 декабря 2011

У меня такая простая ситуация:

@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;
}

Подпрограмма для вставки нового клиента:

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

Как мне решить эту проблему?Я думаю, что проблема связана с CascadeType.SAVE_UPDATE, но мне это нужно ... Спасибо всем.


ОБНОВЛЕНИЕ

Я нашел проблему, и этокак его реплицировать:

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);

Итак, если p и p 1 имеют разные категории, проб не будет, но если p и p1 имеют одну и ту же категорию, у меня будет исключение NonUniqueObjectException, так как одна и та же категория находится в сеансе и в спящем режимепопробуйте сохранить_создать его.

Мне нужен CascadeType.SAVE_UPDATE как в сущности «Продукт», так и в категории, так как я могу решить эту проблему?Спасибо.

1 Ответ

2 голосов
/ 08 декабря 2011

Проблема, вероятно, из-за плохого управления транзакциями. Весь ваш код должен выполняться за одну транзакцию, а не за транзакцию для каждого вызова DAO. Уровень обслуживания должен быть тем, который разграничивает транзакцию, а не уровень DAO.

Я могу представить, что делает Hibernate:

  1. Вы звоните dao.get(Product.class, 2). Это возвращает отдельный продукт, указывающий на категорию с ID 67 (например). Результат отсоединяется, потому что транзакция заканчивается, когда завершается вызов DAO.
  2. Вы звоните dao.get(Product.class, 3). Это возвращает отдельный продукт, указывающий на категорию с ID 67 (например). Но поскольку вызов выполняется в другой транзакции, вы получаете второй, другой экземпляр Category (с тем же идентификатором, что и у первой).
  3. Вы вызываете saveOrUpdate для продукта. Это относится к категории, и Hibernate должен, таким образом, присоединить категорию 1 и категорию 2 к сеансу. Поскольку оба они имеют одинаковый идентификатор, один из каскадов может быть не выполнен.

Если вы используете одну транзакцию для получения обоих продуктов, у них будет одинаковый экземпляр категории, и проблема не возникнет. Вызов merge вместо saveOrUpdate также должен работать: Hibernate скопирует состояние обеих категорий в третью, прикрепленную. Но правильное решение - использовать транзакцию, включающую оба вызова в DAO.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...