【发布时间】:2021-01-01 13:10:36
【问题描述】:
我正在尝试克隆一个帖子对象。为此,我将其 ID 设置为 null,然后使用 entityManager 将其分离,然后保存。
关于帖子 cmets,我让 CascadeType.DETACH 完成传播更改的工作。考虑下一段代码来说明问题:
// PostService.java
private void clonePost(Post post) {
post.setId(null);
post.getComments().forEach(comment -> comment.setId(null));
entityManager.detach(post); // CascadeType will take care of detaching the comments
postRepository.save(post); // now we have a duplicated post with the same comments
// Since it's a detached entity and has no ID, Hibernate will treat it as a
// new entity and save it creating a new record
}
现在,有一个问题。这是Post 类:
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
@SequenceGenerator(allocationSize = 50, initialValue = 1)
private Long id;
// ...
@OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Comment> comments = new ArrayList<>();
// ...
@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.DETACH)
@JoinTable(name = "post_tags",
joinColumns = @JoinColumn(name = "post_fk", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "post_tag_fk", referencedColumnName = "id"))
@Fetch(FetchMode.SUBSELECT)
private Set<PostTag> tags = new HashSet<>();
}
帖子tags 使用@ManyToMany 的单向关系,Post 是其所有者,并且也是懒惰的获取。通常,分离帖子也会导致标签分离,因为cascade = CascadeType.DETACH设置在@ManyToMany中。
但是,这并没有发生。标签没有被分离。懒惰似乎确实有某种影响,可以防止分离充分传播,因为手动执行或初始化标签可以解决问题:
private void clonePost(Post post) {
// ...
// option 1 - manual detaching
post.getTags().forEach(tag -> entityManager.detach(tag));
// option 2 - initializing tags allows the detach of the post to propagate to them
post.getTags().forEach(tag -> Hibernate.initialize(tag));
entityManager.detach(post);
postRepository.save(post);
}
如果标签设置为急切加载,这两个选项都可以避免:
public class Post {
@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.DETACH)
@JoinTable(name = "post_tags",
joinColumns = @JoinColumn(name = "post_fk", referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "post_tag_fk", referencedColumnName = "id"))
// @Fetch(FetchMode.JOIN) // this fetch mode will also make the tags to load eager
private Set<PostTag> tags = new HashSet<>();
}
为什么会这样?为什么父级分离时CascadeType.DETACH 不传播到延迟加载的@ManyToMany 集合?
【问题讨论】:
标签: spring hibernate spring-data-jpa hibernate-mapping cascade