Hibernate: OneToMany спасает детей каскадно

В классе Parent есть список List. Когда родитель сохранен, дочерние элементы, которые были добавлены или изменены, должны быть сохранены/обновлены с помощью режима гибернации.

Я нашел много объяснений по этому поводу, однако я просто не заставляю его работать.

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.class попробуйте 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);

при промывке, однако, я получаю следующую ошибку:

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)

Кто-нибудь видит мою ошибку?

Большое спасибо!!


person Steve Eastwood    schedule 10.03.2012    source источник
comment
опубликуйте код для child и то, как вы создаете объекты, перед сохранением.   -  person ManuPK    schedule 11.03.2012


Ответы (5)


Я думаю, если вы ответите на вопрос из первого комментария, мы придем к такой ситуации:

  • у вас уже есть постоянный родитель
  • у вас есть новые дочерние объекты, которые еще не были сохранены
  • вы добавляете детей к родителю и делаете saveOrUpdate

В этом случае спящий режим просто каскадирует сохранение или обновление для дочерних элементов, но их нельзя сохранить или обновить, поскольку они еще не были постоянными. И теперь Hibernate просто говорит: «Я не могу обновить непостоянный объект».

В одном предложении: Hibernate каскадирует только то, что выдается каскадно. В этом случае вы выдаете "SAVE_UPDATE", которое затем каскадно передается дочерним элементам. Я думаю, вы ожидали, что Hibernate будет умным и переключится на сохранение для детей здесь. Но здесь Hibernate работает не так, я и раньше сталкивался с подобными ситуациями. Немного запутанно, но если вы когда-то поняли, как работает каскадирование, вы увидите такие ситуации.

person Markus    schedule 11.03.2012

Ваша попытка Parent.class A кажется уже правильной. Но чтобы каскадировать дочерний элемент при сохранении родителя, вы должны поместить каскад на стороне владельца (в режиме «один ко многим» это сущность, имеющая внешний ключ).

Попробуй это

@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;
}
person Joshua H    schedule 26.11.2015
comment
Это работает? Для меня, когда Parent еще не сохранилось - это не работает! Итак, реальное решение выглядит так: в транзакции - сохранить родителя, сохранить дочерний с родителем-связкой - person Alex Efimov; 20.06.2019

Мне нужен был способ вставить объект с новым объединенным (дочерним) объектом вместе. Один из способов сделать это — разделить действия (например, сначала сохранить объединенный объект, а затем сохранить основной объект). Однако Hibernate поддерживает вставку нового объекта с новым объединенным объектом. См. один пример того, как: Как вставлять дочерние элементы OneToMany каскадно.

person Ilanh    schedule 05.08.2012

Попробуй это:

@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/пустой

person Arif Nazar Purwandaru    schedule 28.07.2017

Это только один метод, который работает, как и ожидалось, после многих экспериментов (сохраняет дочерние объекты и автоматически создает отношения). Я надеюсь, что это будет полезно.

@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;
}
person Ilya Lysenko    schedule 06.04.2020