简体   繁体   English

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

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

I actually never quite understood this behavior in hibernate. 我实际上从来没有完全理解这种在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. 但是,如果新Child具有与旧Child相同的唯一键值,则会失败。 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. 因为我不想假设hibernate混淆了发送给DB的语句的顺序,所以我必须假设有关hibernate的其他内容,而事实并非如此。 Any ideas why the latter example works, while the first one doesn't? 任何想法为什么后一个例子有效,而第一个没有?

Hibernate version is 3.5. Hibernate版本是3.5。 DB is Mysql InnoDB DB是Mysql InnoDB

Hibernate doesn't know about, nor respect, all database constraints (eg MySQL unique constraints). Hibernate不了解或尊重所有数据库约束(例如MySQL唯一约束)。 It's a known issue they don't plan on addressing anytime soon. 这是一个众所周知的问题,他们不打算很快解决。

Hibernate has a defined order for 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. 编辑:顺便说一下,定义顺序的原因是这是保证外键约束(他们关心的约束之一)不被违反的唯一方法,即使用户做了无序的事情。

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. PostgreSQL和Oracle支持它们,也许还有其他RDBMS。 Hibernate will issue all statements within a transaction, and deferral will ensure that constraints are enforced upon transaction commit only. Hibernate将在事务中发出所有语句,并且延迟将确保仅在事务提交时强制执行约束。 In PostgreSQL, for example: 在PostgreSQL中,例如:

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

It is not ideal, but it is simple and effective. 它并不理想,但它简单有效。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM