Hibernate и исключение NonUniqueObjectException

у меня есть объект, который содержит два других объекта с отношением @ManyToOne.

@Entity
public class A extends Serializable{

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    @Cascade(CascadeType.SAVE_UPDATE)
    private B b;

    @ManyToOne
    @Cascade(CascadeType.SAVE_UPDATE)
    private C c;

}

Если я попытаюсь сохранить экземпляр A, у которого есть «B_ID» и «C_ID» другой записи A, я получаю исключение:

org.hibernate.NonUniqueObjectException: другой объект с таким же значением идентификатора уже был связан с сеансом

Например:

A table
|   ID  |   B_ID    |   C_ID    |
|    1  |      1    |   null    |    // this works
|    2  |   null    |      1    |    // this works
|    3  |      1    |      x    |    // this throws the exception
|    4  |      x    |      1    |    // this throws the exception

x=any value of existent B/C_ID 

B_ID и C_ID не являются уникальными в моей модели, и (B_ID + C_ID) не является уникальным ограничением!!

Что я могу сделать?

Заранее спасибо.


person blow    schedule 22.08.2010    source источник


Ответы (1)


Hibernate не жалуется здесь на уникальность базы данных, он жалуется, что текущий Session уже содержит объект с тем же идентификатором, что и новый объект, который вы пытаетесь сохранить. Он этого не допустит — у Hibernate есть строгое требование, согласно которому данный идентификатор не может быть представлен двумя разными объектами в рамках одного сеанса.

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

При необходимости вы можете заставить сеанс «забыть» о любых существующих объектах для данного идентификатора (используя Session.evict() в API Hibernate или EntityManager.detach() в API JPA 2.0), но это не очень элегантное решение.

Повторим еще раз: это исключение не имеет ничего общего с ограничениями базы данных, оно связано с тем, что Hibernate защищает согласованность своего внутреннего состояния в памяти.

person skaffman    schedule 22.08.2010
comment
Спасибо, единственные вещи, которые я загружаю из БД, это B и C, и я использую их в объекте A. Но B и C имеют CascadeType.SAVE_UPDATE, поэтому они не сохраняются снова, а только обновляются... надеюсь... - person blow; 28.08.2010
comment
Проблемы возникают и при запуске, когда сессия новая. - person blow; 28.08.2010
comment
удаление каскадных директив работает хорошо, но... ПОЧЕМУ??? Без Cascade.SAVE_UPDATE спящий режим должен попытаться повторно СОХРАНИТЬ A и B, которые уже присутствуют в БД, и поэтому выдать исключение, но вместо этого он работает ... почему? - person blow; 29.08.2010