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

时间:2010-10-26 09:51:18

标签: java hibernate jpa jpa-2.0 one-to-many

从如上所述映射的列表中删除元素时遇到问题。这是映射:

@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-instances并保存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生成的语句(列表中有五个项目,我删除第一个,Hibernate删除带有LAST索引的映射行,并尝试更新剩余的那些,从第一个)。

上面的映射有什么问题?

4 个答案:

答案 0 :(得分:15)

您的映射完全有效,并且作为JPA 2.0实现与EclipseLink一起使用(当然没有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

当删除时,比如列表中的第一项,从连接表中首先delete最后一行(WTF?)Hibernate:

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)。

答案 1 :(得分: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;
}

答案 2 :(得分:0)

答案 3 :(得分:0)

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

为什么?

因为我认为问题只是在表上的约束而不是在休眠状态。

我不明白为什么bar_id

存在唯一约束

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

我认为问题就是这样:)