【问题标题】:Constraint violation in Hibernate unidirectional OneToMany mapping with JoinTable and OrderColumn when removing elements删除元素时使用 JoinTable 和 OrderColumn 的 Hibernate 单向 OneToMany 映射中的约束冲突
【发布时间】:2011-04-30 16:00:04
【问题描述】:

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

@实体 @表(名称=“富”) 类Foo { 私人列表酒吧; @OneToMany @OrderColumn( 名称 = "order_index" ) @JoinTable(name = "foo_bar_map", joinColumns = @JoinColumn(name = "foo_id"), inverseJoinColumns = @JoinColumn(name = "bar_id")) @Fetch(FetchMode.SUBSELECT) 公共列表 getBars() { 返回栏; } }

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

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

鉴于 Hibernate 生成的语句(列表中有五个项目,我删除第一个项目,Hibernate 删除带有 LAST 索引的映射行并尝试更新其余的,从第一个)。

上面的映射有什么问题?

【问题讨论】:

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


    【解决方案1】:

    通常当通过连接表连接时,关系是多对多而不是单对多。试试这个

    @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;
    }
    

    【讨论】:

    • 但从我的领域模型的角度来看,它只是 OneToMany。用 ManyToMany 替换它会利用一些约束,这显然不是我想要的。我希望架构尽可能安全。这里使用 JoinTable 的主要原因是为了跟踪订单索引,这在 Bar 类中不应该知道。
    • 使用标准 JPA,单向 OneToMany 使用连接表进行映射(尽管 Hibernate 允许使用 JoinColumn 在没有连接表的情况下进行映射,现在 JPA 2.0 也支持此功能)。跨度>
    【解决方案2】:

    【讨论】:

    • 但我希望这种关联是单向的。 Bar 类不应该知道 Foo 引用它的任何事情。
    【解决方案3】:

    您的映射完全有效,并且可以与 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_id 上的unique 约束,这种方法听起来不正确并且不起作用。更一般地说,为什么 Hibernate 会搞乱bar_id 而不是更新order_index 列?

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

    【讨论】:

    • 谢谢,是的,这也是我在调试器中花费了一段时间后发现的。作为修复,我现在正在使用 ManyToMany(它放弃了唯一性约束),但感觉很难看。有趣的是,没有其他人遇到过这个问题,因为我认为这是一个常见的用例......
    • @tbh 是的,使关联成为多对多将删除唯一约束(以允许“多”)。但这确实是一种解决方法,Hibernate 应该正确处理一对多。您可能想为 Jira 问题投票 :)
    【解决方案4】:

    不,我不认为这是一个休眠错误,如果您进行搜索,您会看到 Pascal Thivent 引用的这个休眠错误是自 2006 年以来已知的一个错误,并且从未得到解决。

    为什么?

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

    我不明白为什么 bar_id 有唯一约束

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

    我认为问题出在这种方式:)

    【讨论】:

    猜你喜欢
    • 2018-10-14
    • 2011-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-01
    • 2011-12-13
    • 1970-01-01
    • 2020-12-20
    相关资源
    最近更新 更多