2010-10-26 2 views
16

У меня проблема при удалении элементов из списка, отображаемого, как описано выше. Вот отображение:Нарушение ограничений в однонаправленном однонаправленном однонаправленном однонаправленном сопоставлении OneToMany с элементами 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 и пытается обновить оставшиеся, начиная с первого).

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

ответ

14

Ваше отображение полностью действует и работает с 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?

Я считаю, что это в спящем режиме ошибки (представленную как HHH-5694 см HHH-1268 сейчас).

+1

Спасибо, да, это то, что я также узнал после довольно долгого времени, проведенного в отладчике. В качестве исправления я теперь использую ManyToMany (который снижает ограничение уникальности), но он кажется уродливым. Интересно, что никто другой не столкнулся с этой проблемой, так как я считаю это распространенным прецедентом ... – tbk

+0

@tbh Да, чтобы ассоциация «многие-ко-многим» упустила уникальное ограничение (чтобы позволить «многим» «). Но это действительно обходной путь, Hibernate должен правильно обращаться с одним-ко-многим. Возможно, вы захотите проголосовать за вопрос Джиры :) –

0

Обычно при присоединении к таблице соединений отношение 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; 
} 
+0

Но с моей точки зрения модели домена, это просто OneToMany. Замена этого на ManyToMany будет использовать некоторые ограничения, которые явно не то, что я хочу. Я хочу, чтобы схема была максимально безопасной. Основной причиной использования JoinTable здесь является отслеживание индекса заказа, который не должен быть известен в классе Bar. – tbk

+0

Со стандартным JPA однонаправленный OneToMany сопоставляется с использованием таблицы соединений (хотя Hibernate позволяет сопоставить это без таблицы соединений с помощью 'JoinColumn', который теперь также поддерживается в JPA 2.0). –

1

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

Почему?

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

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

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

Я думаю, что проблема в этом случае :)

+0

Когда-нибудь слышали о заказе? Два ограничения ортогональны: определенный порядок и единственность. – tbk