一对多的单向关联

关系数据库模型中的双向关系是什么?让我们快速看一下 -

假设您有两个表 Post 和 Post_Comment。现在您想找出哪些评论与哪个帖子相关联,或者对于一个帖子,有多少条评论与之相关联?您该怎么做?

Image description

以下是基本 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 List postComments = 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):

Image description

我们可以看到 Post 表只有一行,其 id 和 title 均为 1,title 为“Hibernate in action is quite good”。

现在让我们看一下帖子评论表:

Image description

我们有一行 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 管理关系而不需要我们在代码中明确定义此表。

Join Table

我们可以改进吗?额外的桌子会造成额外的负担。是的,我们可以。让我们看看我们如何做到这一点?

@OneToMany(cascade = CascadeType.PERSIST, orphanRemoval = true)
@JoinColumn(name = "post_id")
private List postComments = new ArrayList<>();

`@JoinColumn(name = "post_id")` 此连接列注释告诉我们,我们不需要任何额外的表来进行映射。相反,POST_COMMENT 表将处理它。POST_COMMENT 表中将有一个名为“post_id”的额外列,它将存储该评论的关联帖子 ID,但好消息是 JPA/hibernate 会自行执行此操作。我们不需要更改我们的帖子评论实体。

POST_COMMENT table with POST_ID

让我们在 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);
    }
Image description

现在让我们尝试获取这些评论。

@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 实体也将自动持久化。换句话说:

  • 您不需要明确保存评论。
  • 只要将它们添加到 postComments 列表中并且 Post 被持久化,Hibernate 就会确保评论被保存到数据库中。
  • Post post = new Post();
    PostComment comment = new PostComment();
    comment.setDescription("A new comment");
    
    post.getPostComments().add(comment);
    
    entityManager.persist(post);

    如果没有 CascadeType.PERSIST,您将需要明确保留每个 PostComment