休眠 - @ElementCollection - 奇怪的删除/插入行为

2022-08-31 22:27:20
@Entity
public class Person {

    @ElementCollection
    @CollectionTable(name = "PERSON_LOCATIONS", joinColumns = @JoinColumn(name = "PERSON_ID"))
    private List<Location> locations;

    [...]

}

@Embeddable
public class Location {

    [...]

}

给定以下类结构,当我尝试将新位置添加到人员的位置列表中时,它始终会导致以下 SQL 查询:

DELETE FROM PERSON_LOCATIONS WHERE PERSON_ID = :idOfPerson

A lotsa' inserts into the PERSON_LOCATIONS table

休眠 (3.5.x / JPA 2) 删除给定人员的所有关联记录,并重新插入所有以前的记录以及新记录。

我的想法是,位置上的等于/哈希码方法可以解决问题,但它并没有改变任何东西。

任何提示都是值得赞赏的!


答案 1

这个问题在关于JPA维基教科书的页面中以某种方式进行了解释:ElementCollection

集合表中的主键

JPA 2.0 规范未提供在 中定义 的方法。但是,若要删除或更新元素集合映射的元素,通常需要一些唯一键。否则,在每次更新时,JPA 提供程序都需要从实体CollectionTable 中删除所有内容,然后重新插入这些值。因此,JPA 提供程序很可能会假设 中所有字段的组合与外键 ((s)) 组合在一起都是唯一的。然而,这可能是低效的,或者如果很大或很复杂,这可能就不可行。IdEmbeddableEmbeddableJoinColunmEmbeddable

这正是这里发生的事情(粗体部分)(Hibernate不会为集合表生成主键,并且无法检测集合的哪个元素已更改,并将从表中删除旧内容以插入新内容)。

但是,如果定义一个(指定用于维护列表持久顺序的列 - 这是有道理的,因为您使用的是 ),Hibernate 将创建一个主键(由顺序列和联接列组成),并且能够在不删除整个内容的情况下更新集合表。@OrderColumnList

如下所示(如果要使用默认列名称):

@Entity
public class Person {
    ...
    @ElementCollection
    @CollectionTable(name = "PERSON_LOCATIONS", joinColumns = @JoinColumn(name = "PERSON_ID"))
    @OrderColumn
    private List<Location> locations;
    ...
}

引用


答案 2

除了 Pascal 的答案之外,您还必须将至少一列设置为 NOT NULL:

@Embeddable
public class Location {

    @Column(name = "path", nullable = false)
    private String path;

    @Column(name = "parent", nullable = false)
    private String parent;

    public Location() {
    }

    public Location(String path, String parent) {
        this.path = path;
        this.parent= parent;
    }

    public String getPath() {
        return path;
    }

    public String getParent() {
        return parent;
    }
}

这个要求记录在 AbstractPersistentCollection 中

HHH-7072 等情况的解决方法。如果集合元素是完全由可为 null 的属性组成的组件,则我们当前必须强制重新创建整个集合。有关详细信息,请参阅在抽象收集容器构造函数中使用 hasNotNullableColumns。为了逐行删除,这将需要类似“WHERE ( COL = ?OR ( COL 为空且 ? 为空 ) )“,而不是当前的”WHERE COL = ?”(对于大多数数据库,空值失败)。请注意,参数必须绑定两次。在我们最终在ORM 5+中将“参数绑定点”概念添加到AST之前,处理这种类型的条件非常困难或不可能。强制娱乐并不理想,但在ORM 4中并不是任何其他选择。


推荐