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/


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