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

最近因为项目的原因,所以需要使用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会生成怎样的数据表?

注意,因为我原来的数据库中已经有了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;

下面我们介绍双向关联,

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/

 

版权声明:著作权归作者所有。

写得不错呀
相关推荐

Linux使用halt命令关闭运行中操作系统

halt命令用来关闭正在运行的Linux操作系统。halt命令会先检测系统的runlevel,若runlevel为0或6,则关闭系统,否则即调用shutdown来关闭系统。 语法 halt [-n] [-w] [-d] [-f] [-i] [-p] 选项说明 -d:不要在wtmp中记录;-f:不论目前的runlevel为何,不调用shutdown即强制关闭系统;-i:在halt之前,关闭全部的

SpringCloud配置网关安全

网关安全 微服务整体架构图 相关功能组件 服务注册与发现:consul,etcd,zookeeper,eureka 配置中心:Spring Cloud Config,携程的阿波罗,duic OAuth2 OAuth(Open Authorization,开放授权)是为用户资源的授权定义了一个安全、开放及简单的标准,第三方无需知道用户的账号及密码,就可获取到用户的授权信息

Java关键字transient

transient修饰的属性将不被串行化。使用transient:实现接口Serilizable的对象序列化是,添加了transient的属性不会被添加到序列化的对象中。它只会在内存里使用。transient只能修饰类成员变量,不能修饰类方法和方法内的变量对于静态变量,不管是否使用了transient修饰,都不会被序列化对于实现Externalizable的类,它的所有内容都不会自动序列化,它需要

Pycharm关闭Scientific mode

Pycharm3 新增了(科学模式)Scientific mode。在普通的项目里如果不需要使用到Scientific mode,可以选择关闭它。View > 去掉Scientific Mode的勾选Settings > Tools > Python Scientific

HTML 5关闭表单的自动填充(Autocompletion)

在HTML 5可以使用autocomplete属性关闭表单的自动填充。autocomplete="off" autocomplete可以在两个地方使用:form标签和input等输入标签form标签在form标签添加autocomplete="off"是关闭整个表单的自动填充。<form method="post"

关于Java异常

在这篇博文里,我们思考下检查异常 (checked exception)和非检查异常(unchecked exception),特别是它们在函数式编程里的影响。十几年前Java出现时,在当时它是相当有创意的。特别是它的异常处理机制,相对先前的C/C++有了很大的提高。例如,读取文件可以出现很多异常:文件可以不存在,可以为只读等等。相关Java的伪代码类似于:File&