一对多的单向关联
关系数据库模型中的双向关系是什么?让我们快速看一下 -
假设您有两个表 Post 和 Post_Comment。现在您想找出哪些评论与哪个帖子相关联,或者对于一个帖子,有多少条评论与之相关联?您该怎么做?
以下是基本 Post 实体表
@Entity public class Post { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Column private String title; public Post() { } // ignoring setter and getter }
以下是基本的帖子评论实体表
@Entity public class PostComment { @Id @GeneratedValue(strategy = GenerationType.AUTO) private int id; @Column private String description; public PostComment() { } // ignoring setter and getter
目前,Post 和 PostComment 实体之间没有任何关系。现在让我们在它们之间建立单向关系。
**需要考虑的关键案例:**
以所有者身份发帖(一对多):
这意味着 Post 实体将管理关系。每个 Post 可以有多个 PostComment 条目。
作为所有者发布评论(多对一):
这里,PostComment 实体将管理关系。每条评论将链接到单个帖子。
让我们从实现案例 1:以所有者身份发帖开始。
@Entity public class Post { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @Column private String title; @OneToMany(cascade = CascadeType.PERSIST, orphanRemoval = true) private ListpostComments = new ArrayList<>(); public Post() { } public void addComment(PostComment comment) { postComments.add(comment); } public void removeComment(PostComment comment) { postComments.remove(comment); } // ignoring setter and getter }
现在我们在这里做了什么?
我们添加了一个列表,并使用 `@OneToMany` 告诉 Post 它可以有零个、一个或多个帖子评论。单个 Post 可以有多条评论。我们在后面的两处中设置了 `cascade = CascadeType.PERSIST, orphanRemoval = true`。
`@OneToMany(级联 = CascadeType.PERSIST,orphanRemoval = true)
私人列表postComments = new ArrayList<>();`
我们还添加了两种方法,可以让我们更轻松地添加/删除评论
public void addComment(PostComment comment) { postComments.add(comment); } public void removeComment(PostComment comment) { postComments.remove(comment); }
好的,我们几乎完成了实体设计(尚未完全完成)。让我们看看如何保存/保留带有评论的帖子。
@Service public class PostService { @Autowired EntityManager eManager; @Transactional public void addPost1() { Post post = new Post(); post.setTitle("Hibernate in action is quite good"); PostComment comment = new PostComment(); comment.setDescription("Recommended"); post.addComment(comment); eManager.persist(post); } }
这里我们创建了一篇文章,然后设置标题并添加评论。然后调用“eManager.persist(post)”,这将保存该文章及其相关评论。很简单,对吧!!!让我们看一下数据库(H2):
我们可以看到 Post 表只有一行,其 id 和 title 均为 1,title 为“Hibernate in action is quite good”。
现在让我们看一下帖子评论表:
我们有一行 id 为 1,描述为推荐。太棒了!但是 POST_POST_COMMENTS 表是什么?我们没有在代码库中明确定义它。
解释如下:
当我们在帖子和评论之间建立一对多关系时,每条评论都会使用 post_id 链接到帖子。这会在 post_id 和 post_comment_id 之间创建映射。但这个映射存储在哪里?
Hibernate 会自动创建一个连接表,将两个实体/表的名称(POST 和 POST_COMMENTS)组合在一起。该表包含两列:
Post 表的主键 (POST_ID)。
Post_Comments 表中的主键 (POST_COMMENT_ID)。
通过这种方式,Hibernate 管理关系而不需要我们在代码中明确定义此表。
我们可以改进吗?额外的桌子会造成额外的负担。是的,我们可以。让我们看看我们如何做到这一点?
@OneToMany(cascade = CascadeType.PERSIST, orphanRemoval = true) @JoinColumn(name = "post_id") private ListpostComments = new ArrayList<>();
`@JoinColumn(name = "post_id")` 此连接列注释告诉我们,我们不需要任何额外的表来进行映射。相反,POST_COMMENT 表将处理它。POST_COMMENT 表中将有一个名为“post_id”的额外列,它将存储该评论的关联帖子 ID,但好消息是 JPA/hibernate 会自行执行此操作。我们不需要更改我们的帖子评论实体。
让我们在 ID 为 1 的帖子中添加另一条评论。
@Transactional public void addCommentOnPost() { Post post = eManager.find(Post.class, 1); PostComment comment = new PostComment(); comment.setDescription("this book is also for beginner"); post.addComment(comment); eManager.persist(post); }
现在让我们尝试获取这些评论。
@Transactional public void findCommentByPostId() { Post post = eManager.find(Post.class, 1); System.out.println("Number of comment " + post.getPostComments().size()); System.out.println(post.getPostComments()); } /* Number of comment 2 [ PostComment [id=1, description=Recommended], PostComment [id=2, description=this book is also for beginner] ] */
**JPA/Hibernate 如何获取评论:**
当您调用 post.getPostComments() 时,JPA/Hibernate 会查询 POST_COMMENT 表。它会检查 post_id 列中是否有与 post_id 匹配的条目(在我们的例子中,post_id=1)。然后它会检索所有匹配的行并将它们映射到 PostComment 对象。这就是获取和返回与帖子相关的两条评论的方式
**orphanRemoval = true**
这确保如果从 postComments 列表中删除 PostComment,它将自动从数据库中删除。本质上:
Post post = entityManager.find(Post.class, 1); PostComment comment = post.getPostComments().get(0); post.getPostComments().remove(comment);
如果未设置 orphanRemoval = true,那么即使从列表中删除 PostComment,它仍会存在于数据库中,从而可能导致出现孤立行。
**CascadeType.PERSIST**
这意味着每当您持久化 Post 实体时,所有关联的 PostComment 实体也将自动持久化。换句话说:
Post post = new Post(); PostComment comment = new PostComment(); comment.setDescription("A new comment"); post.getPostComments().add(comment); entityManager.persist(post);
如果没有 CascadeType.PERSIST,您将需要明确保留每个 PostComment