Нарушение ограничений в однонаправленном сопоставлении OneToMany в Hibernate с помощью JoinTable и OrderColumn при удалении элементов
-
26-09-2019 - |
Вопрос
У меня возникла проблема при удалении элементов из списка, сопоставленного, как описано выше.Вот отображение:
@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 удаляет строку сопоставления с индексом ПОСЛЕДНИЙ и пытается обновить оставшиеся, начиная с первого) .
Что не так с приведенным выше отображением?
Решение
Ваше сопоставление полностью допустимо и работает с 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 (сообщается как ХХХ-5694, видеть ХХХ-1268 сейчас).
Другие советы
Обычно при присоединении через присоединиться таблица отношения - многотоманы не оретоманы. Попробуй это
@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;
}
Я думаю, что вам нужно, это правильное обратное отображение.
http://docs.jboss.org/hibernate/core/3.3/reference/en/html/tuTorial.html#tutorialial- ассоциации-Пидарнациональные
Нет, я не думаю, что это BUG Hibernate, и, как вы увидите, если вы ищете эту ошибку Hibernate, цитируют Pascal Thivent, является ошибкой, известной с 2006 года и никогда не была решена.
Почему ?
Потому что я думаю, что проблема только в ограничении на столе, а не в гибернации.
Я не понимаю, почему есть уникальное ограничение на bar_id
Использование индекса заказа означает, что ваша коллекция является списком (а не набор!). И список - это коллекция, где вы можете указать индекс к элементам, которые вы добавляете (он соответствует заказанному).
Разница между списком и набором заключается в том, что вы и можете одни и те же данные дважды (или более), но те же данные будут в разных показателях. Тогда вы можете иметь одну и ту же BAR_ID разные индексы, вам не нужно указывать уникальное ограничение на bar_id. И первичный ключ не может быть (foo_id, order_index) привести к тому, что список шаблонов разрешает одинаковые данные на разных индексах. Может быть, ваш PK должен быть (foo_id, bar_id, order_index)?
Я думаю, что проблема в этом пути :)