繁体   English   中英

如何避免与 JPA 级联重复?

[英]How to avoid duplicates with JPA cascades?

我有一个多对一关系子实体实体:

@Entity class Parent {
  // ...
  @ManyToOne((cascade = {CascadeType.ALL})
  private Child child;
  // ...
}

Child有一个独特的字段:

@Entity class Child {
  // ...
  @Column(unique = true)
  private String name;
  // ...
}

当我需要一个新的Child 时,我首先询问ChildDAO

Child child = childDao.findByName(name);
if(child == null) {
  child = new Child(name);
}

Parent parent = new Parent();
parent.setChild(child);

问题是,如果我喜欢上面两次(与Child同名),并且最后只保留Parent ,我会得到一个约束异常。 这似乎很正常,因为最初数据库中没有指定名称的子项。

问题是,我不确定避免这种情况的最佳方法是什么。

您正在使用new Child()创建两个 Child 的非持久性实例,然后将它们放入两个不同的 parent。 当您持久化 Parent 对象时,两个新的 Child 实例中的每一个都将通过级联持久化/插入,每个实例都有不同的@Id 名称上的唯一约束随后中断。 如果您正在执行 CascadeType.ALL,那么每次执行new Child()您可能会得到一个单独的持久对象。

如果您真的希望将两个 Child 实例视为具有相同 ID 的单个持久对象,则需要单独持久化它以与持久性上下文/会话相关联。 随后对childDao.findByName调用将刷新插入并返回您刚刚创建的新 Child,因此您不会执行new Child()两次。

你得到这个是因为你试图持久化一个已经存在的对象(相同的 ID)。 您的级联可能不是持久的,它是 MERGE/UPDATE/REMOVE/DETACH。 避免这种情况的最佳方法是正确设置级联或手动管理级联。 基本上来说,级联持续是通常的罪魁祸首。

你可能想要这样的东西:

//SomeService--I assume you create ID's on persist
saveChild(Parent parent, Child child)
{
  //Adding manually (using your cascade ALL)
  if(child.getId() == null) //I don't exist persist
    persistence.persist(child);
  else
    persistence.merge(child); //I'm already in the DB, don't recreate me

  parent.setChild(child);
  saveParent(parent); //using a similar "don't duplicate me" approach.
}

如果让框架管理级联,级联可能会非常令人沮丧,因为如果它正在缓存(特别是多对一关系中的集合),您偶尔会错过更新。 如果您没有明确保存父级并允许框架处理级联。 一般来说,我允许在一段关系中来自我父母的 Cascade.ALL 和来自我孩子的 DELETE/PERSIST 的所有级联。 我是 Eclipselink 用户,所以我在 Hibernate/other 方面的经验有限,我无法谈论缓存。 * 真的,想想看——如果你在拯救一个孩子,你真的想拯救所有与它相关的物体吗? 另外,如果你要添加一个孩子,你不应该通知父母吗? *

在您的情况下,我只会在我的 Dao/Service 中使用“saveParent”和“saveChild”方法,以确保缓存正确且数据库正确以避免头痛。 手动管理意味着您将拥有绝对的控制权,而不必依赖级联来完成繁重的工作。 在一个方向上,它们很棒,一旦它们来到“上游”,你就会有一些意想不到的行为。

如果您创建两个对象,并让 JPA 自动持久化它们,那么您将在数据库中获得两行。 因此,您有两个选择:不要创建两个对象,或者不要让 JPA 自动持久化它们。

为了避免创建两个对象,您必须安排您的代码,以便如果两个父项尝试创建具有相同名称的子项,他们将获得相同的实际实例。 您可能需要一个WeakHashMap (范围为当前请求,可能通过局部变量引用),以名称为键,父母可以在其中查找新孩子的名称以查看孩子是否已经存在。 如果没有,他们可以创建对象并将其放入地图中。

为避免 JPA 自动持久化对象,请删除级联并在创建后立即使用persist手动将对象添加到上下文中。

由于持久化上下文基本上是一个附加到数据库的经过精心设计的 WeakHashMap,因此归根结底,这些方法非常相似。

您正在设置一个子对象,然后如果坚持父对象,您将在数据库中存储一个指向不存在的子对象的寄存器。

当您创建一个新对象时,它必须由 entityManager 以相同的方式管理,当您使用 DAO“查找”一个对象时,它必须从 DB 获取寄存器并将该对象放入 entityManager 上下文中。

尝试先持久化或合并子对象。

暂无
暂无

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

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