JPA中OneToMany关系时,单向关联和双向关联如何使用?

时间:2022-06-01 21:39:36

最近因为项目的原因,所以需要使用JPA来对数据进行持久化,因此对这部分内容做了一些总结,特别是对@OneToMany这个注解有了一些学习和理解:

我们知道,注解@OneToMany是对一对多关系的一个注解,这里我们需要注意的是:

一对多的关系分为两类,一类是单向关系,另一类就是双向关系

这里就有一个问题啦?啥叫单向关系(unidirectional),啥叫双向关系(bidirectional)?

首先解释一下,什么叫做单向关系,所谓单向关系,在@OneToMany这里就是指从一方能够访问到多方的数据,但是不能反着访问。

举个例子

@Entity
@Table(name = "post1")
public class Post {
    @Id
    @Column(name="id")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String title;
    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    private List<PostComment> comments = new ArrayList<>();
    public Post() {
    }
    public static Post newPost(String title) {
        Post post = new Post();
        post.setTitle(title);
        return post;
    }
    public Post addComment(PostComment comment) {
       this.comments.add(comment);
       return this;
    }
    public Post removeComment(PostComment comment) {
        this.comments.remove(comment);
        return this;
    }
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public List<PostComment> getComments() {
        return comments;
    }
    public void setComments(List<PostComment> comments) {
        this.comments = comments;
    }
}
import javax.persistence.*;
 
import java.util.Objects;
@Table(name = "post_comment")
@Entity
public class PostComment {
    @Id
    @Column(name="id")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String review;
    public PostComment() {
    }
    public static PostComment newPostComment(String review) {
        PostComment comment = new PostComment();
        comment.setReview(review);
        return comment;
    }
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getReview() {
        return review;
    }
    public void setReview(String review) {
        this.review = review;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        PostComment comment = (PostComment) o;
        return review.equals(comment.review);
    }
    @Override
    public int hashCode() {
        return Objects.hash(review);
    }
}

如上面的代码所述,Post能够访问PostComment的数据,但是反过来不行,我们可以看看JPA会生成怎样的数据表?

JPA中OneToMany关系时,单向关联和双向关联如何使用?

注意,因为我原来的数据库中已经有了post表,所以这里我将Post实体对应的表修改成了post1,此时总共生成了三张表post1和post_comment以及一张关联表post1_comments。

这里问一个小问题?关联表的表名是根据什么关系生成的?

另一个需要注意的地方是在注解@OneToMany中有一个orphanRemoval = true,这个的作用是当在实体Post中操作PostComment对象是,更新实体的时候会相应的删除不存在的PostComment

有的人可能会说,这种方式生成三个表,性能多差呀,我们有办法,这次我们生成两个表,通过一个外键字段进行关联,下面我们贴出另一种单向关联的方式

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name = "post1")
public class Post {
    @Id
    @Column(name="id")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String title;
    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinColumn(name = "post_id")
    private List<PostComment> comments = new ArrayList<>();
    public Post() {
    }
    public static Post newPost(String title) {
        Post post = new Post();
        post.setTitle(title);
        return post;
    }
    public Post addComment(PostComment comment) {
       this.comments.add(comment);
       return this;
    }
    public Post removeComment(PostComment comment) {
        this.comments.remove(comment);
        return this;
    }
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public List<PostComment> getComments() {
        return comments;
    }
    public void setComments(List<PostComment> comments) {
        this.comments = comments;
    }
}
import javax.persistence.*;
import java.util.Objects;
@Table(name = "post1_comment")
@Entity
public class PostComment {
    @Id
    @Column(name="id")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String review;
    @Column(name="post_id")
    private Long postId;
    public PostComment() {
    }
    public static PostComment newPostComment(String review) {
        PostComment comment = new PostComment();
        comment.setReview(review);
        return comment;
    }
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getReview() {
        return review;
    }
    public void setReview(String review) {
        this.review = review;
    }
    public Long getPostId() {
        return postId;
    }
    public void setPostId(Long postId) {
        this.postId = postId;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        PostComment comment = (PostComment) o;
        return review.equals(comment.review);
    }
    @Override
    public int hashCode() {
        return Objects.hash(review);
    }
}

这次我们在关联关系的一方增加了一个注解@JoinColumn(name = "post_id"),并定义了一个外键post_id,同时在多方增加了一个字段

@Column(name="post_id")

private Long postId;

JPA中OneToMany关系时,单向关联和双向关联如何使用?

下面我们介绍双向关联,

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity(name = "Post2")
@Table(name = "post2")
public class Post {
    @Id
    @Column(name="id")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String title;
    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true,mappedBy = "post")
    private List<PostComment> comments = new ArrayList<>();
    public Post() {
    }
    public static Post newPost(String title) {
        Post post = new Post();
        post.setTitle(title);
        return post;
    }
    
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public List<PostComment> getComments() {
        return comments;
    }
    public void setComments(List<PostComment> comments) {
        this.comments = comments;
    }
}package io.majing.message.domain.post.bidirectional;
import javax.persistence.*;
import java.util.Objects;
@Table(name = "post2_comment")
@Entity(name = "PostComment2")
public class PostComment {
    @Id
    @Column(name="id")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String review;
    @ManyToOne(fetch = FetchType.LAZY)
    private Post post;
    public PostComment() {
    }
    public static PostComment newPostComment(String review) {
        PostComment comment = new PostComment();
        comment.setReview(review);
        return comment;
    }
    public Post getPost() {
        return post;
    }
    public void setPost(Post post) {
        this.post = post;
    }
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getReview() {
        return review;
    }
    public void setReview(String review) {
        this.review = review;
    }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        PostComment comment = (PostComment) o;
        return review.equals(comment.review);
    }
    @Override
    public int hashCode() {
        return Objects.hash(review);
    }
}

这里需要注意的是为了实现创想关联,我们在一方的注解@OneToMany添加了一个属性mappedBy = "post",这里表明了关联关系,同时在多方的实体中添加了一个

@ManyToOne(fetch = FetchType.LAZY)

private Post post;

也就是说在多方添加了一个对一方关系对象的引用,这样我们就建立了双向关联。

这里特别需要注意的一点就是我们在一方添加多方对象时,务必需要建议多方对象和一方的关系,同理,在删除对象的时候,也要删除双方关系的关联,在上面具体对应的代码就是

public Post addComment(PostComment comment) {
        this.comments.add(comment);
        comment.setPost(this);
        return this;
    }
    public Post removeComment(PostComment comment) {
        this.comments.remove(comment);
        comment.setPost(null);
        return this;
    }

至此,我们解释完了在使用JPA的时候我们怎么样建立单向关系和双向关系,每种方法有每种方法的优缺点,需要根据情况选择使用。

参考链接https://vladmihalcea.com/the-best-way-to-map-a-onetomany-association-with-jpa-and-hibernate/