使用JPA从子对象到父对象的级联

时间:2015-11-28 10:45:36

标签: java jpa orm persistence eclipselink

我有以下情况。

我们的域中有两个对象,它们形成父子关系。

人是父母,角色是孩子。这两个对象从同一个祖先延伸出来。

不是将关系建模为双向,而是仅包含父表示为long的对象Id。

父母有一个@oneToMany映射到孩子。

我们遇到的问题如下:域层当前创建并持久保存子项,然后只是将父项的id更新为自身。

然而,这种方法的问题在于,已经加载到持久化上下文中的Person不会使用此新角色进行重新刷新。当我们尝试在单个事务中编排多个操作时,这种方法在我们的应用程序中造成了破坏。我们也无法利用二级缓存。我们希望创建一个双向关系,但是我不清楚处理子对象的最佳方法是什么。

我看到的所有建议都表明应保存Parent对象,然后应该级联到子对象。还建议对所有级联类型都应遵循这种方法。所以对孩子的改变是通过父母来保持的。

我并没有对这种方法提出质疑,但是,由于已经有了实施,从孩子到父母的级联是否可行?我想这应该适用于Merge和Refresh?除了添加@ManyToOne注释之外,这似乎是一种可行的方法,用于将持久上下文与对象的更改同步,而无需更改底层实现。

我欢迎任何意见或建议。

2 个答案:

答案 0 :(得分:1)

不,从孩子到父母的级联根本不是一个好主意。

我建议你采取另一种方法,并将孩子与父母之间的联系起诉@ManyToOne@ManyToOne关系是最自然的关联,因为它遵循RDBMS采用的FK方法。

由于您已经使用@OneToMany关联,因此您只需将其转换为mappedBy关联,并添加从父级到子级的级联。此方法允许您单独保存Child。您唯一需要注意的是,如果EntityManager已加载父项和子项,则同步双方。但是,如果您只是在没有获取父项的情况下加载Child,则只需单独使用Child(例如将Parent设置为null)。

答案 1 :(得分:0)

您正在猜测,正如Javadoc所说,cascade操作必须级联到关联目标。”但是,请确保您了解mappedBy定义了拥有实体拥有实体是实际执行持久操作的实体,除非被级联设置覆盖。在这种情况下,Child是拥有实体。

@Entity
public class Parent {
    @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @OneToMany(mappedBy="parent")
    private Set<Child> children;

在创建Parent子级并将其设置为Set然后保存Parent时,Parent上的级联设置起作用。然后,保存操作将从Parentchildren级联。这是cascade设置的更典型的预期使用案例。但是,它确实会导致数据库操作自动发生,这并不总是一件好事。

当孩子坚持下来时,将在孩子上进行级联设置,因此您可以在其中放置cascade批注,但请继续阅读...

@Entity
public class Child {
    @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long id;

    @ManyToOne(cascade=CascadeType.ALL)
    private Parent parent;

通过保留孩子,您将同时保留父母和孩子。

tx.begin();
Parent p = new Parent();
Child c = new Child(); 
c.setParent(p);
em.persist(c);
tx.commit();

当您删除子级时,它会同时删除父级和子级。

tx.begin();
Child cFound = em.find(Child.class, 1L);
em.remove(cFound);
tx.commit();
em.clear();

这是您遇到的问题。如果您有多个孩子会怎样?

em.clear();
tx.begin();
p = new Parent();
Child c1 = new Child(); 
Child c2 = new Child(); 
c1.setParent(p);
c2.setParent(p);
em.persist(c1);
em.persist(c2);
tx.commit();

一切顺利,直到您删除children之一

em.clear();
tx.begin();
cFound = em.find(Child.class, 2L);
em.remove(cFound);
tx.commit();

然后,当级联传播到integrity constraint violation时,您将得到一个Parent,但数据库中还有第二个Child。当然可以通过一次提交删除所有子级来治愈它,但这有点混乱了吗?

从概念上讲,人们倾向于认为传播是从ParentChild的,因此以其他方式传播是非常违反直觉的。此外,如果您不想仅仅因为商店出售了他或她的所有书就删除作者,该怎么办?在这种情况下,您可能会混合级联,有时是从子级到父级,在其他情况下是从父级到子级。

通常,我认为最好在数据库代码中保持精确。比起我可能会或可能不会意识到的其他地方隐式地执行其他数据库操作,要更容易阅读,理解和维护专门先保存父项然后再保存孩子的代码。