java Hibernate JPA:@OneToMany 删除旧的,插入新的而不刷新

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17410868/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-01 01:55:49  来源:igfitidea点击:

Hibernate JPA: @OneToMany delete old, insert new without flush

javahibernatejpa

提问by st-h

I actually never quite understood this behavior in hibernate. I am using a @OneToMany relationship in a Entity called 'Parent', which is annotated like this:

我实际上从未完全理解休眠中的这种行为。我在名为“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;

Now I want to do the following within one transaction:

现在我想在一笔交易中执行以下操作:

  • Get the parent entity
  • iterate through the list of children
  • delete one of the children
  • insert a new child
  • 获取父实体
  • 遍历子项列表
  • 删除其中一个孩子
  • 插入一个新的孩子

So, basically I am just entirely replacing one of the children.

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

As far as I understand this problem, I should be able to do something like this: (please note that this is just some java pseudocode to illustrate the problem)

据我了解这个问题,我应该能够做这样的事情:(请注意,这只是一些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);
}

However, this fails in case the new Child has the same unique key values as the old one. So, basically it seems like the old child entity isn't removed properly, before the new one is persisted.

但是,如果新子项与旧子项具有相同的唯一键值,则此操作将失败。所以,基本上,在新的子实体被持久化之前,旧的子实体似乎没有被正确删除。

If I add a entityManager.flush() between deleting the old child and persisting the new child like this:

如果我在删除旧子项和保留新子项之间添加 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);
}

Everything works fine. The child is deleted before the new one is inserted, as it should.

一切正常。在插入新的之前,孩子会被删除,这是应该的。

As I don't want to asume that hibernate mixes up the order of the statements that are sent to the DB, there must be something else I am assuming about hibernate which isn't the case. Any ideas why the latter example works, while the first one doesn't?

因为我不想假设 hibernate 混淆了发送到数据库的语句的顺序,所以我对 hibernate 的假设肯定不是这样。有什么想法为什么后一个例子有效,而第一个例子无效?

Hibernate version is 3.5. DB is Mysql InnoDB

Hibernate 版本是 3.5。DB 是 Mysql InnoDB

回答by Pace

Hibernate doesn't know about, nor respect, all database constraints (e.g. MySQL unique constraints). It's a known issue they don't plan on addressing anytime soon.

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

Hibernate has a defined orderfor the way operations occur during a flush.

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

Entity deletions will always happen after inserts. The only answers I know about are to remove the constraint or add the additional flush.

实体删除总是在插入后发生。我知道的唯一答案是删除约束或添加额外的冲洗。

EDIT: By the way, the reason for the defined order is that this is the only way to guarantee foreign key constraints (one of the constraints they DO care about) aren't violated, even if the user does something out of order.

编辑:顺便说一句,定义顺序的原因是这是保证外键约束(他们关心的约束之一)不被违反的唯一方法,即使用户不按顺序执行某些操作。

回答by Nik?a Baldun

For the sake of future readers, one way to resolve this issue is to use deferred constraints. PostgreSQL and Oracle support them, maybe other RDBMS' too. Hibernate will issue all statements within a transaction, and deferral will ensure that constraints are enforced upon transaction commit only. In PostgreSQL, for example:

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

ALTER TABLE company
    ADD CONSTRAINT name_unique UNIQUE (name) DEFERRABLE INITIALLY DEFERRED;

It is not ideal, but it is simple and effective.

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