【发布时间】:2021-05-08 16:32:18
【问题描述】:
假设有 2 个实体,它们代表博客文章及其 cmets。一个博客可以有多个 cmets:
@Entity
public class Post {
@Id
@GeneratedValue
private Long id;
private String name;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "post")
private List<Comment> comments;
}
@Entity
@IdClass(CommentPK.class)
public class Comment {
@Id
private Long id;
@Id
@ManyToOne
private Post post;
private String content;
}
class CommentPK implements Serializable {
private Long post;
private Long id;
}
我希望 Comment 具有 (post_id,comment_id) 的复合 PK,其中 comment_id 是一个帖子中的序列,例如:
| post_id | comment_id | content |
| 1 | 1 | 123 |
| 1 | 2 | 456 |
| 1 | 3 | Hello SO |
| 2 | 1 | New Post | << Post #2 has comment IDs starting from 1 again
虽然映射有效,但我无法保存帖子,因为评论没有 ID。这就是我添加实体侦听器的原因:
@PrePersist
@PreUpdate
public void preProcess(Post post) {
int id = 1;
for (Comment comment : post.getComments()) {
comment.setPost(post);
comment.setId(id++);
}
}
它适用于第一个帖子持久化,但是如果我向持久化帖子添加新的 cmets,则不会调用实体侦听器。我也尝试使用类似的逻辑为 Comment 添加实体侦听器,但它只是没有被调用
@Test
public void testPersistAndUpdate() {
Post post = new Post();
post.setName("Test");
Comment comment1 = new Comment();
comment1.setPost(post);
comment1.setContent("Comment 1");
List<Comment> comments = new ArrayList<>();
comments.add(comment1);
post.setComments(comments);
Post savedPost = postRepository.saveAndFlush(post); // Spring Data JPA repository, all OK for now
// create new Comment
Comment comment2 = new Comment();
comment2.setPost(post);
comment2.setContent("Comment 2");
savedPost.getComments().add(comment2);
savedPost = postRepository.saveAndFlush(savedPost); // here entity listener is not invoked and exception is thrown
}
我遇到了一个异常:
javax.persistence.PersistenceException: org.hibernate.HibernateException: 复合标识符的任何部分都不能为空
问题
-
对此类关系建模的常见做法是什么? (仍然需要复合键)因为现在通过 JPA/Hibernate 处理它感觉很不舒服
-
如果保留示例中的映射,当
Post属性未更新但Comment是新的并且应该保留时,是否可以使@PrePersist为Comment工作?
【问题讨论】:
标签: java hibernate jpa spring-data-jpa