我实际上从未在hibernate中完全理解这种行为。 我在名为“Parent”的实体中使用@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混淆了发送给DB的语句的顺序,所以我必须假设有关hibernate的其他内容,而事实并非如此。任何想法为什么后一个例子有效,而第一个没有?
Hibernate版本是3.5。 DB是Mysql InnoDB
答案 0 :(得分:23)
Hibernate不了解也不尊重所有数据库约束(例如MySQL唯一约束)。这是一个众所周知的问题,他们不打算很快解决这个问题。
Hibernate对刷新期间的操作方式有defined order。
实体删除将始终在插入后发生。我知道的唯一答案是删除约束或添加额外的刷新。
编辑:顺便说一句,定义顺序的原因是,这是保证外键约束(他们关心的约束之一)不被违反的唯一方法,即使用户做了一些事情。顺序。
答案 1 :(得分:15)
为了将来的读者,解决此问题的一种方法是使用延迟约束。 PostgreSQL和Oracle支持它们,也许是其他RDBMS&#39;太。 Hibernate将在事务中发出所有语句,并且延迟将确保仅在事务提交时强制执行约束。在PostgreSQL中,例如:
ALTER TABLE company
ADD CONSTRAINT name_unique UNIQUE (name) DEFERRABLE INITIALLY DEFERRED;
这不是理想的,但它简单而有效。