我的父实体在 ManyToOne 关系中有 Child 实体:
@Entity class Parent {
// ...
@ManyToOne((cascade = {CascadeType.ALL})
private Child child;
// ...
}
儿童有一个唯一字段:
@Entity class Child {
// ...
@Column(unique = true)
private String name;
// ...
}
当我需要一个新的孩子时,我先问 ChildDAO :
Child child = childDao.findByName(name);
if(child == null) {
child = new Child(name);
}
Parent parent = new Parent();
parent.setChild(child);
问题是,如果我喜欢两次(孩子的名称相同),并且只保留父最后,我得到一个约束异常。这似乎很正常,因为最初在数据库中没有具有指定名称的子项。
问题是,我不确定避免这种情况的最佳方法是什么。
答案 0 :(得分:9)
您正在创建两个具有new Child()
的Child的非持久性实例,然后将它们放在两个不同的Parent中。当您持久保存Parent对象时,两个新的Child实例中的每一个都将通过级联持久化/插入,每个实例具有不同的@Id
。然后,对名称的唯一约束会中断。如果您正在使用CascadeType.ALL,那么每次执行new Child()
时,您可能会获得一个单独的持久对象。
如果您确实希望将两个Child实例视为具有相同ID的单个持久对象,则需要将其单独保留以与持久性上下文/会话关联。对childDao.findByName
的后续调用将刷新插入并返回刚创建的新Child,因此您不会执行new Child()
两次。
答案 1 :(得分:6)
你得到这个是因为你试图坚持一个已经存在的对象(相同的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.
}
如果让框架管理它,级联可能会非常令人沮丧,因为如果它是缓存的话,你偶尔会错过更新(特别是在ManyToOne关系中的Collections)。如果您没有明确保存父级并允许框架处理级联。一般来说,我允许来自我父母的Cascade.ALL和我孩子们的DELETE / PERSIST级联。我是一个Eclipselink用户,所以我对Hibernate / other的体验有限,我无法谈论缓存。 *真的,想一想 - 如果你正在拯救一个孩子,你真的想保存所有与之相关的对象吗?另外,如果你要加孩子,你不应该通知家长吗? *
在你的情况下,我只需要在我的Dao / Service中使用“saveParent”和“saveChild”方法,以确保缓存正确并且数据库正确以避免头痛。手动管理意味着您将拥有绝对的控制权,您不必依赖级联来完成您的脏工作。在单向方向上它们非常棒,它们“上游”的那一刻你将会有一些意想不到的行为。
答案 2 :(得分:4)
如果您创建了两个对象,并让JPA自动保留它们,那么您将在数据库中获得两行。因此,您有两种选择:不要制作两个对象,或者不要让JPA自动保留它们。
为了避免制作两个对象,您必须安排代码,这样如果两个Parent尝试创建具有相同名称的Children,它们将获得相同的实际实例。你可能想要一个WeakHashMap(作用于当前请求的范围,可能是由本地变量引用),按名称键入,其中父母可以查找他们的新Child名称以查看Child是否已经存在。如果没有,他们可以创建对象并将其放在地图中。
为避免让JPA自动保留对象,请删除级联并使用persist在创建后立即手动将对象添加到上下文中。
由于持久化上下文基本上是附加到数据库的欺骗性WeakHashMap,因此这些方法在归结为它时非常相似。
答案 3 :(得分:1)
您正在设置子对象,如果您将要存储寄存器的父项存储在指向不存在的子项的数据库中。
当你创建一个新对象时,它必须由entityManager以与使用DAO“找到”对象时相同的方式管理它必须从DB获取寄存器并将对象放在entityManager上下文中。
尝试先保留或合并子对象。