Hibernate: OneToMany save children by cascade
Asked Answered
A

5

28

In the class Parent there is a list List. When the parent is saved the children which have been added or changed shall be save / updated by hibernate.

I've found many explanations on this, however, I just don't get it to work.

Parent.class try 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.class try 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;

children are added to a new parent. Afterwards both are saved by

sessionFactory.getCurrentSession().saveOrUpdate(parent);

when flushing, though, I get the following error:

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)

Does anybody see my mistake?

Thank you a lot!!

Ayers answered 10/3, 2012 at 21:48 Comment(1)
publish the code for child and how you are constructing the objects, before the save.Libelant
R
19

I guess if you answer the question from the first comment, we will come to this situation:

  • you have an already persisted parent
  • you have new child objects, that were not yet persisted
  • you add the children to the parent and do saveOrUpdate

In that case hibernate just cascades the save or update to the children, but they cannot be saved or updated as they have not been persistent yet. And now Hibernate says simply "I cannot update a non persistent entity"

In one sentence: Hibernate only cascades what it is issued to cascade. In this case you issue a "SAVE_UPDATE", which is cascaded then further to the children. I guess you expected Hibernate to be smart and switch to persist for the children here. But this is not how Hibernate works here, I came across similar situations before, too. A bit confusing, but if you once got how cascading works, you will see such situations.

Reefer answered 11/3, 2012 at 8:52 Comment(0)
C
18

Your Parent.class try A seems already correct. But to cascade the child while saving the parent, you must put the cascade on the owner side (in one-to-many, it is the entity that have Foreign Key).

Try this

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

and in your Child.class

@Entity
public class Child {
    @ManyToOne
    @Cascade(value={org.hibernate.annotations.CascadeType.ALL})
    @JoinColumn(name="PARENT_ID")
    protected Parent parent;
}
Cinematography answered 26/11, 2015 at 9:2 Comment(1)
Is that working? For me, when Parent not persisted yet - it is not working! So, the real solution looks: in transaction - save parent, save child with linking parentPleochroism
C
12

Try this:

@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;
}

Usage

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);
}

Note: Don't forget to set the parent on child object or you will get TABLE_CHILD.PARENT_ID null/empty

Chlordane answered 28/7, 2017 at 1:59 Comment(0)
T
6

I needed a way to insert an entity with a new joined (children) entity together. One way to do it is by separate the actions (e.g. to save the joined entity first and then to save the main entity). However, Hibernate support inserting a new entity with new joined entity. See one example how: How to insert OneToMany children by cascade

Tobin answered 5/8, 2012 at 7:9 Comment(1)
your link is brokenMannequin
D
3

This is only one method, which is worked as expected for me after many experiments (saves child entities and creates relations automatically). I hope it will be helpful.

@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;
}
Dogmatist answered 6/4, 2020 at 19:55 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.