Нарушение ограничения в однонаправленном отображении OneToMany в Hibernate с помощью JoinTable и OrderColumn при удалении элементов - PullRequest
16 голосов
/ 26 октября 2010

У меня проблема при удалении элементов из списка, сопоставленного, как описано выше.Вот отображение:

@Entity
@Table( name = "foo")
class Foo {

    private List bars;

    @OneToMany
    @OrderColumn( name = "order_index" )
    @JoinTable( name = "foo_bar_map", joinColumns = @JoinColumn( name = "foo_id" ), inverseJoinColumns =  @JoinColumn( name = "bar_id" ) )
    @Fetch( FetchMode.SUBSELECT )
    public List getBars() {
        return bars;
    }
}

Вставка экземпляров Bar и сохранение Foo работает нормально, но когда я удаляю элемент из списка и сохраняю снова, уникальное ограничение bar_id в таблице сопоставления нарушается.Следующие SQL-операторы выдают hibernate, и они выглядят довольно странно:

LOG:  execute : delete from foo_bar_map where foo_id=$1 and order_index=$2
DETAIL:  parameters: $1 = '4', $2 = '6'
LOG:  execute S_5: update foo_bar_map set bar_id=$1 where foo_id=$2 and order_index=$3
DETAIL:  parameters: $1 = '88', $2 = '4', $3 = '0'
ERROR:  duplicate key value violates unique constraint "foo_bar_map_bar_id_key"

Ошибка вполне имеет смысл, учитывая операторы, сгенерированные Hibernate (в списке пять элементов, я удаляю первыйone и Hibernate удаляют строку сопоставления с индексом LAST и пытается обновить оставшиеся, начиная с первого).

Что не так с отображением выше?

Ответы [ 4 ]

15 голосов
/ 27 октября 2010

Ваше отображение полностью корректно и работает с EclipseLink как реализация JPA 2.0 (конечно, без аннотации Fetch), но на самом деле не работает с Hibernate.

Вот DDL с Hibernate:

create table foo_bar_map (foo_id bigint not null, bar_id bigint not null, order_index integer not null, primary key (foo_id, order_index), unique (bar_id))
alter table foo_bar_map add constraint FK14F1CB7FA042E82 foreign key (bar_id) references Bar4022509
alter table foo_bar_map add constraint FK14F1CB7B6DBCCDC foreign key (foo_id) references Foo4022509

Допустим, Foo#1 содержит список с Bar#1, Bar#2, Bar#3, таблица соединения содержит:

foo_id | bar_id | order_index
     1 |      1 |           1
     1 |      2 |           2
     1 |      3 |           3

При удалении, скажем, первого элемента из списка, Hibernate сначала delete последняя строка (WTF?) Из таблицы объединения:

foo_id | bar_id | order_index
     1 |      1 |           1
     1 |      2 |           2

А затем пытается update столбец bar_id в объединяемой таблице вместо order_index (WTF !?) для отражения «нового» порядка элементов в списке.Первый (схематично):

foo_id | bar_id | order_index
     1 |      2 |           1
     1 |      2 |           2

, где следующий шаг приведет к:

foo_id | bar_id | order_index
     1 |      2 |           1
     1 |      3 |           2

Очевидно, что этот подход не звучит правильно и не работает из-за ограничения unique на bar_id.В более общем смысле, почему, черт возьми, Hibernate связывается с bar_id вместо обновления столбца order_index?

Я считаю, что это ошибка Hibernate (сообщается как HHH-5694 , см. HHH-1268 сейчас).

0 голосов
/ 08 сентября 2011

Нет, я не думаю, что это ошибка гибернации, и, как вы увидите, если вы будете искать, эта ошибка гибернации, цитируемая Паскалем Thivent, является ошибкой, известной с 2006 года и никогда не решавшейся.

Почему?

Потому что я думаю, что проблема только в ограничениях на таблицу, а не в спящий режим.

Я не понимаю, почему существует уникальное ограничение для bar_id

Использование индекса заказа означает, что ваша коллекция - это список (а не набор!). А List - это коллекция, в которой вы можете указать индекс для добавляемых вами элементов (он соответствует OrderColumn).
Разница между списком и набором заключается в том, что вы и можете использовать одни и те же данные дважды (или более), но одни и те же данные будут иметь разные индексы. Тогда вы можете иметь тот же bar_id для разных индексов, вам не нужно указывать уникальное ограничение для bar_id. И первичный ключ не может быть (foo_id, order_index), потому что шаблон списка разрешает одни и те же данные с разными индексами. Может быть, ваш ПК должен быть (foo_id, bar_id, order_index)?

Я думаю, что проблема заключается в следующем:)

0 голосов
/ 26 октября 2010

Я думаю, что вам нужно правильное обратное отображение.
http://docs.jboss.org/hibernate/core/3.3/reference/en/html/tutorial.html#tutorial-associations-bidirectional

0 голосов
/ 26 октября 2010

Обычно при соединении через соединительную таблицу отношения ManyToMany, а не OneToMany. Попробуйте это

@ManyToMany
@OrderColumn( name = "order_index" )
@JoinTable( name = "foo_bar_map", joinColumns = @JoinColumn( name = "foo_id" ), inverseJoinColumns =  @JoinColumn( name = "bar_id" ) )
@Fetch( FetchMode.SUBSELECT )
public List getBars() {
    return bars;
}
...