Нарушение ограничений в однонаправленном однонаправленном сопоставлении OneToMany Hibernate с помощью JoinTable и OrderColumn при удалении элементов
У меня проблема при удалении элементов из списка, отображаемого, как описано выше. Вот отображение:
@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 выдаются спящим режимом, и они выглядят довольно странно:
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 (в списке пять элементов, я удаляю первый, а Hibernate удаляет строку сопоставления с индексом LAST и пытается обновить остальные, начиная с первый).
Что не так с отображением выше?
Ответы
Ответ 1
Ваше сопоставление полностью действует и работает с 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 сейчас.)
Ответ 2
Нет, я не думаю, что это ошибка спящего режима, и, как вы увидите, если вы выполняете поиск, эта ошибка в спящем режиме, указанная Паскалем Тивентом, является ошибкой, известной с 2006 года и никогда не была решена.
Почему?
Причина: я думаю, что проблема заключается только в ограничениях таблицы, а не в спящем режиме.
Я не понимаю, почему существует уникальное ограничение на bar_id
Использование индекса заказа означает, что ваша коллекция - это список (а не набор!).
А List - это коллекция, в которой вы можете указать индекс добавляемым элементам (это соответствует OrderColumn).
Разница между списком и множеством заключается в том, что вы и можете использовать одни и те же данные дважды (или более), но одни и те же данные будут иметь разные индексы.
Тогда у вас может быть тот же bar_id, что и другие индексы, вам не нужно указывать уникальное ограничение на bar_id.
И первичный ключ не может быть (foo_id, order_index), потому что список шаблонов разрешает одни и те же данные при разных индексах.
Может быть, ваш ПК должен быть (foo_id, bar_id, order_index)?
Я думаю, что проблема такова:)
Ответ 3
Обычно при подключении через таблицу соединений отношение 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;
}
Ответ 4
Я предполагаю, что вам нужно правильное обратное отображение.
http://docs.jboss.org/hibernate/core/3.3/reference/en/html/tutorial.html#tutorial-associations-bidirectional