web-dev-qa-db-fra.com

Hibernate: OneToMany sauve les enfants par cascade

Dans la classe Parent, il y a une liste List. Lorsque le parent est enregistré, les enfants qui ont été ajoutés ou modifiés doivent être enregistrés/mis à jour par hibernate.

J'ai trouvé de nombreuses explications à ce sujet, mais je n'arrive pas à le faire fonctionner.

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

les enfants sont ajoutés à un nouveau parent. Ensuite, les deux sont enregistrés par

sessionFactory.getCurrentSession().saveOrUpdate(parent);

lors du rinçage, j'obtiens l'erreur suivante:

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)

Quelqu'un voit-il mon erreur?

Merci beaucoup!!

19
Steve Eastwood

Je suppose que si vous répondez à la question du premier commentaire, nous arriverons à cette situation:

  • vous avez un parent déjà persistant
  • vous avez de nouveaux objets enfants, qui n'étaient pas encore persistants
  • vous ajoutez les enfants au parent et faites saveOrUpdate

Dans ce cas, hibernate ne fait que cascader l'enregistrement ou la mise à jour pour les enfants, mais ils ne peuvent pas être enregistrés ou mis à jour car ils n'ont pas encore été persistants. Et maintenant Hibernate dit simplement "Je ne peux pas mettre à jour une entité non persistante"

En une phrase: Hibernate ne fait que cascader ce qui est émis en cascade. Dans ce cas, vous émettez un "SAVE_UPDATE", qui est ensuite mis en cascade vers les enfants. Je suppose que vous vous attendiez à ce qu'Hibernate soit intelligent et que le commutateur persiste pour les enfants ici. Mais ce n'est pas comme cela que Hibernate fonctionne ici, j'ai déjà rencontré des situations similaires auparavant. Un peu déroutant, mais si vous avez une fois compris comment fonctionne la cascade, vous verrez de telles situations.

15
Markus

Votre essai Parent.class A semble déjà correct. Mais pour mettre en cascade l'enfant tout en sauvegardant le parent, vous devez mettre la cascade du côté propriétaire (en un-à-plusieurs, c'est l'entité qui a la clé étrangère).

Essaye ça

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

et dans votre Child.class

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

J'avais besoin d'un moyen d'insérer une entité avec une nouvelle entité (enfants) jointe ensemble. Une façon de procéder consiste à séparer les actions (par exemple, pour enregistrer d'abord l'entité jointe, puis pour enregistrer l'entité principale). Cependant, Hibernate prend en charge l'insertion d'une nouvelle entité avec une nouvelle entité jointe. Voir un exemple comment: Comment insérer des enfants OneToMany par cascade

6
Ilanh

Essaye ça:

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

Remarque: N'oubliez pas de définir le parent sur l'objet enfant ou vous obtiendrez TABLE_CHILD.PARENT_ID null/vide

5