【发布时间】:2013-06-28 22:50:35
【问题描述】:
实际上我从来没有完全理解休眠中的这种行为。 我在名为“父”的实体中使用@OneToMany 关系,其注释如下:
@OneToMany(cascade = {CascadeType.ALL, CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE }, orphanRemoval = true)
@JoinColumn(name = "entity_id", insertable = true, updatable = true, nullable = false)
private List<Child> children;
现在我想在一笔交易中执行以下操作:
- 获取父实体
- 遍历子列表
- 删除其中一个孩子
- 插入一个新的孩子
所以,基本上我只是完全替换了其中一个孩子。
据我了解这个问题,我应该能够做这样的事情: (请注意,这只是一些说明问题的java伪代码)
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void deleteAndAdd(Long parentId, Long childId) {
Parent parent = entityManager.find(parentId);
for (Iterator it = parent.children.iterator(); it.hasNext();) {
Child child = it.next();
if (child.id == childId) {
it.remove();
}
}
Child newChild = new Child();
parent.children.add(newChild);
}
但是,如果新 Child 与旧 Child 具有相同的唯一键值,则此操作会失败。所以,基本上看起来旧的子实体在新实体被持久化之前没有被正确删除。
如果我在删除旧子项和保留新子项之间添加一个 entityManager.flush(),如下所示:
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void deleteAndAdd(Long parentId, Long childId) {
Parent parent = entityManager.find(parentId);
for (Iterator it = parent.children.iterator(); it.hasNext();) {
Child child = it.next();
if (child.id == childId) {
it.remove();
}
}
entityManager.flush();
Child newChild = new Child();
parent.children.add(newChild);
}
一切正常。孩子在插入新的之前被删除,这是应该的。
由于我不想假设 hibernate 混淆了发送到数据库的语句的顺序,所以我对 hibernate 的假设肯定不是这样。任何想法为什么后一个示例有效,而第一个示例无效?
休眠版本是 3.5。 DB是Mysql InnoDB
【问题讨论】:
-
但是,如果新 Child 与旧 Child 具有相同的唯一键值,则此操作将失败。您是说 child 的主键与已经退出的主键相同吗?跨度>
-
其实没有。例如,Child 上有一个复合唯一键,它由“entity_id”FK 和实体的另一个值(例如“名称”)组成。如果我删除名为“child1”的孩子并添加一个具有相同名称“child1”的新孩子,我正在观察所描述的行为。两个孩子的 FK 显然是相同的,以及我设置为相同值的名称。但是,如果在持久化新的之前将旧的删除,这应该可以正常工作。
-
与其删除孩子,不如用新的数据更新它,如果两者具有相同的主键?
-
在我看来冲洗模式不是自动的,您可以将冲洗模式设置为自动吗?另外,当您说它不起作用时,发生了什么?有什么例外吗?
-
@spaniard 有效点。但不幸的是,这并不能解释我遇到的行为。而且 PK 不一定相同,但我同意在大多数情况下仍然可以更新而不是在需要时删除和插入新对象。但是,如果我们进一步讨论,问题通常从接口规范开始。