Hibernate:OneToMany级联保存子项

28

在Parent类中,有一个叫做List的列表。当父类被保存时,通过hibernate添加或更改的子类也应该被保存/更新。

我已经找到了很多关于这方面的解释,但是我无法让它正常工作。

Parent.class尝试A

@Entity
public class Parent {
// id and other attributes
@OneToMany(mappedBy = "parent")
@org.hibernate.annotations.Cascade(org.hibernate.annotations.CascadeType.ALL)
protected List<child> children;

尝试在Parent类中调用B

@Entity
public class Parent {
// id and other attributes
  @OneToMany(mappedBy = "parent", cascade = { javax.persistence.CascadeType.ALL },
 orphanRemoval = true)
 @org.hibernate.annotations.Cascade({ 
 org.hibernate.annotations.CascadeType.PERSIST,
 org.hibernate.annotations.CascadeType.MERGE,
 org.hibernate.annotations.CascadeType.REFRESH,
 org.hibernate.annotations.CascadeType.SAVE_UPDATE,
 org.hibernate.annotations.CascadeType.REPLICATE,
 org.hibernate.annotations.CascadeType.LOCK,
 org.hibernate.annotations.CascadeType.DETACH })
protected List<child> children;

将子节点添加到一个新的父节点。然后两者都会被保存。

sessionFactory.getCurrentSession().saveOrUpdate(parent);

当我执行flush操作时,却遇到了以下错误:

org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: de.pockettaxi.backend.model.ride.RideSingle
at org.hibernate.engine.ForeignKeys.getEntityIdentifierIfNotUnsaved(ForeignKeys.java:243)
at org.hibernate.type.EntityType.getIdentifier(EntityType.java:456)
at org.hibernate.type.ManyToOneType.isDirty(ManyToOneType.java:265)
at org.hibernate.type.ManyToOneType.isDirty(ManyToOneType.java:275)
at org.hibernate.type.TypeHelper.findDirty(TypeHelper.java:295)
at org.hibernate.persister.entity.AbstractEntityPersister.findDirty(AbstractEntityPersister.java:3378)
at org.hibernate.event.def.DefaultFlushEntityEventListener.dirtyCheck(DefaultFlushEntityEventListener.java:520)
at org.hibernate.event.def.DefaultFlushEntityEventListener.isUpdateNecessary(DefaultFlushEntityEventListener.java:230)
at org.hibernate.event.def.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:154)
at org.hibernate.event.def.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:219)
at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:99)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1216)

有人能看出我的错误吗?

非常感谢!!


2
在保存之前,发布 child 代码以及您如何构建对象的方式。 - ManuPK
5个回答

19

我猜如果你回答第一个评论中的问题,我们会来到这种情况:

  • 您已经持久化了父对象
  • 您有新的子对象,尚未持久化
  • 您将孩子添加到父母并执行saveOrUpdate

在这种情况下,Hibernate只是将保存或更新级联到子元素,但它们无法保存或更新,因为它们还没有被持久化。现在Hibernate简单地说“我不能更新非持久化实体”

一句话:Hibernate只级联它所需的级联操作。在这种情况下,您发出了一个"SAVE_UPDATE",然后进一步级联到孩子。我猜你希望Hibernate在这里聪明地切换到对孩子进行持久化。但这不是Hibernate在这里的工作方式,我以前也遇到过类似的情况。有点令人困惑,但是如果您一旦了解了级联操作的工作原理,您就会看到这样的情况。


17

你的 Parent.class 的尝试 A 似乎已经正确了。但是,如果要在保存 Parent 时级联更新 Child,请将级联设置在关系的拥有者端(在一对多中,即具有外键的实体)。

尝试这个:

@Entity
public class Parent {
    @OneToMany(mappedBy = "parent")
    protected List<Child> children;
}

并且在你的Child.class中

@Entity
public class Child {
    @ManyToOne
    @Cascade(value={org.hibernate.annotations.CascadeType.ALL})
    @JoinColumn(name="PARENT_ID")
    protected Parent parent;
}

2
这个能正常工作吗?对于我来说,当父对象还没有被持久化时,它是无法工作的!因此,真正的解决方案是:在事务中保存父对象,将子对象与父对象关联后再保存。 - Oleksandr Yefymov

11

试一下这个:

@Entity
@Table(name = "TABLE_PARENT")
public class Parent{
    @OneToMany(mappedBy="parent", cascade=CascadeType.PERSIST)
    private List<Child> children;
}

@Entity
@Table(name = "TABLE_CHILD")
public class Child{
    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="PARENT_ID")
    private Parent parent;
}

使用方法

public void save(){
  Parent parent = new Parent();
  List<Child> children = new ArrayList<>();
  Child child = new Child();
  child.setParent(parent);
  children.add(child);
  parent.setChildren(children);

  parentRepository.save(parent);
}

注意:不要忘记在子对象上设置父级,否则您将得到TABLE_CHILD.PARENT_ID为null/空。


6

我需要一种方法,可以将一个实体与一个新的关联(子)实体一起插入。其中一种方法是分开操作(例如先保存关联实体,然后再保存主实体)。但是,Hibernate支持插入带有新关联实体的新实体。以下是一个示例:如何通过级联插入OneToMany子项


你的链接损坏了。 - Alireza Farokhi

3

这只是一种方法,在经过多次实验后为我所预期的工作效果(自动保存子实体并创建关系)。希望它对您有所帮助。

@Entity
public class Parent {
    @Id
    private Long id;

    @JoinColumn(name = "parentId")
    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    private Set<Child> children;
}

@Entity
public class Child {
    @Id
    private Long id;
    private Long parentId;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接