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

nih*_*t84 39 java orm hibernate jpa jpa-2.0

@Entity
public class Person {

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

    [...]

}

@Embeddable
public class Location {

    [...]

}
Run Code Online (Sandbox Code Playgroud)

给定以下类结构,当我尝试将新位置添加到Person的位置列表时,它总是会导致以下SQL查询:

DELETE FROM PERSON_LOCATIONS WHERE PERSON_ID = :idOfPerson
Run Code Online (Sandbox Code Playgroud)

A lotsa' inserts into the PERSON_LOCATIONS table
Run Code Online (Sandbox Code Playgroud)

Hibernate(3.5.x/JPA 2)删除给定Person的所有相关记录,并重新插入所有以前的记录,再加上新记录.

我认为Location上的equals/hashcode方法可以解决问题,但它没有改变任何东西.

任何提示都表示赞赏!

Pas*_*ent 59

ElementCollection在JPA wikibook 的页面中以某种方式解释了该问题:

CollectionTable中的主键

JPA 2.0规范没有提供定义Id中的方法 Embeddable.但是,要删除或更新ElementCollection映射的元素, 通常需要一些唯一的密钥.否则,在每次更新时,JPA提供程序都需要从CollectionTablefor中删除所有内容 Entity,然后重新插入值.因此,JPA提供商将最有可能假设所有的字段的组合Embeddable是独一无二的,结合外键(JoinColunm(S)).然而,这可能是低效的,或者如果Embeddable大或复杂则不可行.

这正是(粗体部分)这里发生的事情(Hibernate没有为集合表生成主键,也无法检测集合中的哪个元素发生了变化,并且会从表中删除旧内容以插入新内容).

但是,如果你定义一个@OrderColumn(指定一个用于维护列表的持久顺序的列 - 这在你使用a之后就有意义了List),Hibernate将创建一个主键(由order列和连接列组成)并且将能够更新集合表而不删除整个内容.

这样的东西(如果你想使用默认的列名):

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

参考

  • 我发现了相同的"全部删除","插入行1","插入行2"...... sql行为.我正在使用OpenJPA2.2.2并将@Id注释插入到可嵌入的子实体中没有帮助.OrderColumn需要一个新的整数sortorder db列,我的DB模式不是由这个JPA应用程序维护的,所以它可能不是一个选项. (2认同)

Vla*_*cea 7

除了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;
    }
}
Run Code Online (Sandbox Code Playgroud)

AbstractPersistentCollection中记录了此要求:

适用于HHH-7072等情况的解决方法.如果collection元素是一个完全由可空属性组成的组件,那么我们当前必须强制重新创建整个集合.有关详细信息,请参阅AbstractCollectionPersister构造函数中hasNotNullableColumns的使用.为了逐行删除,这需要像"WHERE(COL =?OR(COL是null和?是null))"这样的SQL,而不是当前的"WHERE COL =?" (对于大多数DB而言,无效为null).请注意,param必须绑定两次.在我们最终将"参数绑定点"概念添加到ORM 5+中的AST之前,处理这种类型的条件要么极其困难,要么不可能.强制娱乐并不理想,但在ORM 4中没有任何其他选择.