Hibernate - @ElementCollection - 奇怪的删除/插入行为
Posted
技术标签:
【中文标题】Hibernate - @ElementCollection - 奇怪的删除/插入行为【英文标题】:Hibernate - @ElementCollection - Strange delete/insert behavior 【发布时间】:2011-04-14 03:53:03 【问题描述】:@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 的所有关联记录并重新插入所有以前的记录以及新记录。
我认为 Location 上的 equals/hashcode 方法可以解决问题,但它并没有改变任何东西。
感谢任何提示!
【问题讨论】:
我遇到了相反的问题 - 当我更新父级时,Hibernate 没有删除任何内容。这是我在网上找到的唯一相关帖子,包括 JBoss 的 Hibernate 文档。结果是由于现在未知的原因,我将父级上的集合变量声明为final
,而 Hibernate 默默地什么也没做。我拿出了final
,它开始很好地更新和删除。
我发现fetch = FetchType.EAGER
在简单的列表操作中也会发生这种情况(不是更新或删除)l。 @OrderColumn 似乎没有效果
【参考方案1】:
JPA wikibook 关于ElementCollection
的页面以某种方式解释了该问题:
Primary keys in CollectionTable
JPA 2.0 规范没有 提供一种方法来定义
Id
Embeddable
。 但是,删除或 更新一个元素ElementCollection
映射,有些独特 密钥通常是必需的。否则, 在每次更新时,JPA 提供者都会 需要从CollectionTable
为Entity
,和 然后将值插入回去。因此, JPA 提供者很可能会假设 所有的组合Embeddable
中的字段是唯一的, 结合外键 (JoinColunm
(s))。然而,这可能是 低效,或者只是不可行,如果Embeddable
很大或很复杂。
这正是(粗体部分)这里发生的事情(Hibernate 不会为集合表生成主键,并且无法检测到集合的哪些元素发生了变化,并且将从表中删除旧内容以插入新内容)。
但是,如果你定义了一个@OrderColumn
(指定一个用于维护列表的持久顺序的列 - 这很有意义,因为你使用的是List
),Hibernate将创建一个主键(由order 列和join 列组成),并且能够在不删除整个内容的情况下更新集合表。
类似这样的东西(如果你想使用默认的列名):
@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 维基书 Java Persistence/ElementCollection【讨论】:
任何想法如何使用休眠 xml 映射创建这样一个初级密钥? 我发现了相同的“全部删除”、“插入第 1 行”、“插入第 2 行”... sql 行为。我正在使用 OpenJPA2.2.2 并将 @Id 注释插入可嵌入的子实体并没有帮助。 OrderColumn 需要一个新的整数 sortorder db 列,并且我的 DB 模式不是由这个 JPA 应用程序维护的,所以它可能不是一个选项。 这也适用于地图吗?使用 MapKey(Join)Column 等。JPA 总是为我做删除。7 有谁知道这个解决方案是否适用于基本收藏? (例如,如果该字段是 List除了 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 等情况的解决方法。如果集合元素是一个完全由 对于可为空的属性,我们目前必须强制重新创建整个集合。看用途 AbstractCollectionPersister 构造函数中的 hasNotNullableColumns 以获取更多信息。为了删除 逐行,这将需要像“WHERE (COL = ? OR (COL is null AND ? is null))”这样的 SQL,而不是 当前的“WHERE COL =?” (对于大多数数据库来说,null 失败)。注意 参数必须绑定两次。直到我们最终将“参数绑定点”概念添加到 ORM 5+ 中的 AST,处理这种类型的条件要么极其困难,要么不可能。强迫 娱乐并不理想,但在 ORM 4 中并不是真正的任何其他选项。
【讨论】:
天哪,谢谢伙计!我在@ElementCollection
、@OrderColumn
、@Embeddable
和 @UniqueConstraints
一起使用时苦苦挣扎了好几个小时。在删除或更改java.util.List
的顺序时,我违反了唯一约束,因为休眠而不是删除整个集合试图进行更新。将标记为@Embeddable
的类的字段更改为可以为空就可以了!
看起来在最新的 Hibernate (5.2.3) 中,列是否可以为空不再相关。更新似乎每次都会执行,这实际上对我的应用程序来说是不幸的,因为我需要删除所有集合元素然后替换它们,因为我有一个 @UniqueConstraints
,在执行更新时违反了它。考虑使用@OrderColumn
移动到@OneToMany
,并将这个唯一约束定义为主键......好吧......,如果可能的话,让我们看看。
如果您认为这是一个回归问题,您可以添加 Jira 问题【参考方案3】:
我们发现我们定义为 ElementCollection 类型的实体没有定义 equals
或 hashcode
方法并且具有可为空的字段。我们在实体类型上提供了这些(通过@lombok 了解它的价值),它允许 hibernate (v 5.2.14) 识别集合是否脏。
此外,由于我们在一个标有注释@Transaction(readonly = true)
的服务方法中,因此出现了这个错误。由于 hibernate 会尝试清除相关的元素集合并重新插入,因此事务在刷新时会失败,并且由于这条非常难以跟踪的消息而导致事情中断:
HHH000346: Error during managed flush [Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1]
这是我们的实体模型出现错误的示例
@Entity
public class Entity1
@ElementCollection @Default private Set<Entity2> relatedEntity2s = Sets.newHashSet();
public class Entity2
private UUID someUUID;
改成这个
@Entity
public class Entity1
@ElementCollection @Default private Set<Entity2> relatedEntity2s = Sets.newHashSet();
@EqualsAndHashCode
public class Entity2
@Column(nullable = false)
private UUID someUUID;
解决了我们的问题。祝你好运。
【讨论】:
【参考方案4】:我遇到了同样的问题,但想映射一个枚举列表:List<EnumType>
。
我是这样工作的:
@ElementCollection
@CollectionTable(
name = "enum_table",
joinColumns = @JoinColumn(name = "some_id")
)
@OrderColumn
@Enumerated(EnumType.STRING)
private List<EnumType> enumTypeList = new ArrayList<>();
public void setEnumList(List<EnumType> newEnumList)
this.enumTypeList.clear();
this.enumTypeList.addAll(newEnumList);
我的问题是 List
对象总是使用默认设置器替换,因此休眠将它视为一个完全“新”的对象,尽管枚举没有改变。
【讨论】:
以上是关于Hibernate - @ElementCollection - 奇怪的删除/插入行为的主要内容,如果未能解决你的问题,请参考以下文章