删除元素时,Hibernate单向OneToMany映射中的约束违反,包括JoinTable和OrderColumn

从上面描述的映射中删除元素时遇到问题。 这是映射:

 @实体
 @Table(name =“foo”)
 class Foo {

    私人名单栏;

     @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(){
        返回栏;
     }
 }

插入Bar-instances并保存Foo工作正常,但是当我从列表中删除元素并再次保存时,违反了映射表中bar_id的唯一约束。 以下SQL语句是由hibernate发布的,这看起来很奇怪:

日志:执行:从foo_bar_map删除,其中foo_id = $ 1,order_index = $ 2
细节:参数:$ 1 ='4',$ 2 ='6'
日志:执行S_5:更新foo_bar_map set bar_id = $ 1其中foo_id = $ 2和order_index = $ 3
细节:参数:$ 1 = '88',$ 2 ='4',$ 3 ='0'
错误:重复键值违反唯一约束“foo_bar_map_bar_id_key”

这个错误完全有意义,考虑到Hibernate生成的语句(列表中有五个项目,我删除第一个项目,Hibernate删除带有LAST索引的映射行,并尝试更新剩余的项目,从第一个开始) 。

上面的映射有什么问题?

您的映射完全有效,并且与EclipseLink一起用作JPA 2.0实现(当然没有Fetch注释),但实际上Hibernate失败了。

这是带有Hibernate的DDL:

 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#1Bar#2Bar#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 

显然,由于bar_idunique约束,这种方法听起来不对, 不起作用 。 更一般地说,为什么Hibernate会使用bar_id而不是更新order_index列?

我认为这是一个Hibernate错误(报告为HHH-5694 ,现在请参阅HHH-1268 )。

通常在通过连接表加入时,关系是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; } 

不,我不认为这是一个hibernate错误,因为你会看到你是否进行搜索,这个由Pascal Thivent引用的hibernate bug是一个自2006年以来就已知的错误,从未解决过。

为什么?

因为我认为问题只是在表的约束而不是hibernate。

我不明白为什么bar_id有一个独特的约束

使用订单索引意味着您的集合是List(而不是Set!)。 List是一个集合,您可以在其中指定要添加的元素的索引(它对应于OrderColumn)。
List和Set之间的区别在于您可以使用相同的数据两次(或更多),但相同的数据将位于不同的索引中。 然后,您可以使用相同的bar_id作为不同的索引,而不必在bar_id上指定唯一约束。 并且主键不能(foo_id,order_index)导致模式List在不同索引处授权相同的数据。 也许你的PK应该是(foo_id,bar_id,order_index)?

我认为问题就是这样:)