@OneToMany如果不加@JoinColumn,系統會自動在主從表中增加一個中間表。
主表:
@Entity(name = "Post")
public class Post {
@Id
@GeneratedValue
private Long id;
private String title;
@OneToMany(
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();
}
從表:
@Entity(name = "PostComment")
public class PostComment {
@Id
@GeneratedValue
private Long id;
private String review;
}
如果使用下面代碼添加1條主表記錄以及3條從表記錄:
Post post = new Post("First post");
post.getComments().add(
new PostComment("My first review")
);
post.getComments().add(
new PostComment("My second review")
);
post.getComments().add(
new PostComment("My third review")
);
entityManager.persist(post);
實際上系統會執行7條SQL語句
insert into post (title, id) values (‘First post‘, 1) insert into post_comment (review, id) values (‘My first review‘, 2) insert into post_comment (review, id) values (‘My second review‘, 3) insert into post_comment (review, id) values (‘My third review‘, 4) insert into post_post_comment (Post_id, comments_id) values (1, 2) insert into post_post_comment (Post_id, comments_id) values (1, 3) insert into post_post_comment (Post_id, comments_id) values (1, 4)
這樣如果記錄比較多,將會影響到系統性能。我們可以使用@JoinColumn來避免產生中間表:
@JoinColumn(name = "post_id")
但即使是沒有中間表,系統任然會執行7條SQL語句:
insert into post (title, id) values (‘First post‘, 1) insert into post_comment (review, id) values (‘My first review‘, 2) insert into post_comment (review, id) values (‘My second review‘, 3) insert into post_comment (review, id) values (‘My third review‘, 4) update post_comment set post_id = 1 where id = 2 update post_comment set post_id = 1 where id = 3 update post_comment set post_id = 1 where id = 4
如果我們想刪除一條從表記錄
post.getComments().remove(0);
系統任然會執行2條語句:
update post_comment set post_id = null where post_id = 1 and id = 2 delete from post_comment where id=2
要想避免這種情況,就要使用@ManyToOne
@Entity(name = "Post")
@Table(name = "post")
public class Post {
@Id
@GeneratedValue
private Long id;
private String title;
@OneToMany(
mappedBy = "post",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();
public void addComment(PostComment comment) {
comments.add(comment);
comment.setPost(this);
}
public void removeComment(PostComment comment) {
comments.remove(comment);
comment.setPost(null);
}
}
@Entity(name = "PostComment")
@Table(name = "post_comment")
public class PostComment {
@Id
@GeneratedValue
private Long id;
private String review;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "post_id")
private Post post;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof PostComment )) return false;
return id != null && id.equals(((PostComment) o).id);
}
@Override
public int hashCode() {
return 31;
}
}
這樣系統就只會產生4條SQL語句:
insert into post (title, id) values (‘First post‘, 1) insert into post_comment (post_id, review, id) values (1, ‘My first review‘, 2) insert into post_comment (post_id, review, id) values (1, ‘My second review‘, 3) insert into post_comment (post_id, review, id) values (1, ‘My third review‘, 4)
刪除一條從表記錄
PostComment comment1 = post.getComments().get( 0 ); post.removeComment(comment1);
系統也只會執行1條SQL語句:
delete from post_comment where id = 2
但是使用這樣同時使用@OneToMany和@ManyToOne要注意以下幾點:
1. 在從表@ManyToOne中要使用FetchType.LAZY,否則會導致性能降低。
2. 主表中增加了2個方法,addComment和removeComment。
3. 從表重載了equals和hashCode方法。
4. 在使用Json來序列化對象時,會產生無限遞歸(Infinite recursion)的錯誤。這里有2個解決方法:
a. 在@ManyToOne下面使用@JsonIgnore.
b. 在@OneToMany下面使用@JsonManagedReference,在@ManyToOne下面使用@JsonBackReference
@JsonBackReference和@JsonManagedReference:@JsonBackReference標注的屬性在序列化(serialization)時,會被忽略。@JsonManagedReference標注的屬性則會被序列化。在序列化時,@JsonBackReference的作用相當於@JsonIgnore,此時可以沒有@JsonManagedReference。但在反序列化(deserialization)時,如果沒有@JsonManagedReference,則不會自動注入@JsonBackReference標注的屬性;如果有@JsonManagedReference,則會自動注入@JsonBackReference標注的屬性。
@JsonIgnore:直接忽略某個屬性,以斷開無限遞歸,序列化或反序列化均忽略。當然如果標注在get、set方法中,則可以分開控制,序列化對應的是get方法,反序列化對應的是set方法。
