Hibernate JPA:@OneToMany删除old,插入new而不刷新

st-*_*t-h 21 java hibernate jpa

我实际上从来没有完全理解这种在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;
Run Code Online (Sandbox Code Playgroud)

现在我想在一个事务中执行以下操作:

  • 获取父实体
  • 遍历子项列表
  • 删除其中一个孩子
  • 插入一个新的孩子

所以,基本上我只是完全取代其中一个孩子.

据我了解这个问题,我应该可以这样做:(请注意,这只是一些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);
}
Run Code Online (Sandbox Code Playgroud)

但是,如果新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);
}
Run Code Online (Sandbox Code Playgroud)

一切正常.在插入新子项之前删除子项,就像它应该的那样.

因为我不想假设hibernate混淆了发送给DB的语句的顺序,所以我必须假设有关hibernate的其他内容,而事实并非如此.任何想法为什么后一个例子有效,而第一个没有?

Hibernate版本是3.5.DB是Mysql InnoDB

Pac*_*ace 25

Hibernate不了解或尊重所有数据库约束(例如MySQL唯一约束).这是一个众所周知的问题,他们不打算很快解决.

Hibernate 对刷新期间的操作方式有一个已定义的顺序.

实体删除将始终在插入后发生.我知道的唯一答案是删除约束或添加额外的刷新.

编辑:顺便说一下,定义顺序的原因是这是保证外键约束(他们关心的约束之一)不被违反的唯一方法,即使用户做了无序的事情.


Nik*_*dun 20

为了将来的读者,解决此问题的一种方法是使用延迟约束.PostgreSQL和Oracle支持它们,也许还有其他RDBMS.Hibernate将在事务中发出所有语句,并且延迟将确保仅在事务提交时强制执行约束.在PostgreSQL中,例如:

ALTER TABLE company
    ADD CONSTRAINT name_unique UNIQUE (name) DEFERRABLE INITIALLY DEFERRED;
Run Code Online (Sandbox Code Playgroud)

它并不理想,但它简单有效.