假设有 2 个实体,分别代表一篇博客文章及其评论。一个博客可以有多条评论:
@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++);
}
}
它适用于第一个帖子,但是如果我向持久帖子添加新评论,则不会调用实体侦听器。我也尝试使用类似的逻辑为 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 处理它感觉很不舒服
如果保留示例中的映射,是否可以使 @PrePersist
的 Comment
工作,当 Post
属性未更新,但 Comment
是新的,应该保留吗?
答案 0 :(得分:0)
我不认为使用 JPA Callbacks 是解决您问题的正确方法(另请参阅 this article)。我建议您使用 @MapsId 注释。您可以通过以下方式更正映射:
@Entity
public class Post {
@Id
@GeneratedValue
private Long id;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "post")
private List<Comment> comments;
}
@Entity
public class Comment {
@EmbeddedId
CommentPK id;
@MapsId("post")
@ManyToOne
private Post post;
//
}
@Embeddable
class CommentPK implements Serializable {
private Long post;
private Long id;
// getters, setters, equals, hashCode
}
CommentPK.post
值将从 Comment.post
关联派生。