hibernate – @ElementCollection – 奇怪的删除/插入行为

@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 

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

我有这样的想法,位置上的equals / hashcode方法将解决问题,但它并没有改变任何东西。

任何提示都表示赞赏!

这个问题不知何故在JPA wikibook的ElementCollection页面中解释过:

CollectionTable中的主键

JPA 2.0规范没有提供在Embeddable定义Id的方法。 但是,要删除或更新ElementCollection映射的ElementCollection ,通常需要一些唯一的键。 否则,在每次更新时,JPA提供者都需要从EntityCollectionTable删除所有内容,然后再插入值。 因此,JPA提供者很可能会认为Embeddable中的所有字段的组合是唯一的,并与外键( JoinColunm (s))结合使用。 然而,这可能是低效率的,或者如果Embeddable太大或太复杂,就不可行。

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

但是, 如果你定义了一个@OrderColumn (指定一个用于维护列表的持久顺序的列 – 这在你使用List是有意义的),Hibernate将创build一个主键 (由顺序列和连接列 ),并且能够在不删除整个内容的情况下更新集合表。

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

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

参考

  • JPA 2.0规范
    • 第11.1.12节“ElementCollection注释”
    • 第11.1.39节“OrderColumn注解”
  • JPA Wikibook
    • Java Persistence / ElementCollection

除了帕斯卡尔的回答,你还必须至less设置一列为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中logging了这个需求:

HHH-7072等情况的解决方法。 如果集合元素是一个完全由可为空的属性组成的组件,那么我们现在必须强制重新创build整个集合。 有关更多信息,请参阅AbstractCollectionPersister构造函数中的hasNotNullableColumns的使用。 为了逐行删除,需要像“WHERE(COL =?OR(COL is null AND?is null))”这样的SQL,而不是当前的“WHERE COL =?” (对于大多数数据库,失败为空)。 请注意,参数必须绑定两次。 在我们最终将“参数绑定点”的概念添加到ORM 5+中的AST之前,处理这种types的条件要么非常困难,要么是不可能的。 强制娱乐并不理想,但在ORM 4中没有任何其他的select。