【发布时间】:2020-06-09 08:20:21
【问题描述】:
在我在这里的第一个问题之后,问题已经改变,所以我正在创建一个新问题:org.hibernate.TransientObjectException persisting nested children with CascadeType.ALL
我发现我的问题不是保存新实体,而是更新现有实体。
让我们从头开始。
我有一个名为 Human 的类,它有一个狗列表:
@Entity
public class Human {
@Id
@GeneratedValue
private Long id;
@OneToMany(fetch = FetchType.LAZY, cascade = {CascadeType.ALL}, orphanRemoval = true)
private Set<Dog> dogs = new HashSet<>(List.of(new Dog()));
...
}
狗类 Dog 有一个小狗列表:
@Entity
public class Dog {
@Id
@GeneratedValue
private Long id;
@OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.ALL}, orphanRemoval = true)
private Set<Puppy> puppies = new HashSet<>(List.of(new Puppy()));
}
@Entity
public class Puppy {
@Id
@GeneratedValue
private Long id;
}
如果我尝试给他一个新的狗和另一组小狗,我正在尝试让一个现有的人养一只狗,而狗也养一只小狗:
Human human = humanRepository.findById(id); // This human already had a dog and the dog has puppies
Set<Dog> dogs = new HashSet<>();
Dog dog = new Dog();
dog.setPuppies(new HashSet<>(List.of(new Puppy())));
dogs.add(dog);
human.setDogs(dogs);
humanRepository.save(human);
我收到以下错误:
org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.test.Puppy
据我了解,cascade = {CascadeType.ALL} 应该在使用 CrudRepository 保存孩子时自动保留孩子。
编辑:
问题来自于我在更新现有实体时创建了一条带有小狗的新狗。
这是我尝试过的工作示例:
Human human = new Human();
Dog dog = new Dog();
Puppy puppy = new Puppy();
dog.getPuppies().clear();
dog.getPuppies().add(puppy);
human.getDogs().clear();
human.getDogs().add(dog);
humanRepository.save(human);
Human human = humanRepository.findById(id);
human.getDogs().clear();
human.getDogs().add(new Dog());
humanRepository.save(human);
但是无论我检索到的人是否已经有狗,以下方法都不起作用:
Human human = humanRepository.findById(id);
Dog dog = new Dog();
Puppy puppy = new Puppy();
dog.getPuppies().clear();
dog.getPuppies().add(puppy);
human.getDogs().clear();
human.getDogs().add(dog);
humanRepository.save(human);
显然,持久化一个瞬态 Human 将级联持久化到子级和子级的子级。
更新现有 Human 将级联持续到子级而不是子级的子级,从而导致TransientObjectException。
这是预期的行为吗?我应该使用单独的存储库来保存狗和小狗吗?
【问题讨论】:
-
不要这样做
dog.setPuppies和`human.setDogs(dogs);`。而是将其添加到已经存在的持久化和托管集合中(也将其删除)。 -
所以
human.getDogs().clear()然后human.getDogs().add(dog);?我会尽力让你知道,谢谢! -
我仍然遇到同样的错误。我也尝试过清除 HashSet 中的小狗。
human.getDogs().forEach(e -> e.getPuppies().clear());。我还必须将FetchType.EAGER放在任何地方,否则我会收到另一个延迟初始化错误,而@Transactional对此无济于事。 -
您还需要将关系设置为空。否则它不是孤儿
-
你的意思是像
human.getDogs().forEach(dog -> dog.getPuppies().forEach(puppy -> puppy.setDog(null)))这样的东西吗?那是行不通的,因为小狗不引用狗。还是你的意思是别的?
标签: spring spring-data-jpa spring-data