スプリングJPAの双方向関連関係を理解する-永続的な移行カスケード
に入る
以前の位置づけでは,複雑な一対の多関連関係を理解した.
今回のロケーションでは、複雑な関連関係におけるcascadeオプションの理解とクエリーについて説明します.
永続聖殿(CASCADE)とは何ですか?
これは、親エンティティが永続化されると、子エンティティも永続化され、親エンティティが削除されると、子エンティティも削除され、あるエンティティが永続化されると、関連するエンティティも永続化されることを意味します.
今回のプロモーションはJPAについてある程度知っているので、永続的な伝播カスケードとは何か分からない場合は、相応のブログを参考にしてください!
https://willseungh0.tistory.com/67
エンティティ
ERD例
今回の位置決めで使用するエンティティおよび関連関係も、前の位置決めと同じです.
Post
package couch.camping.domain.post.entity;
import couch.camping.domain.base.BaseEntity;
import couch.camping.domain.comment.entity.Comment;
import couch.camping.domain.member.entity.Member;
import couch.camping.domain.postimage.entity.PostImage;
import couch.camping.domain.postlike.entity.PostLike;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Getter
public class Post extends BaseEntity {
@Id @GeneratedValue
@Column(name = "post_id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member member;
@Builder.Default
@OneToMany(mappedBy = "post", cascade = CascadeType.REMOVE, orphanRemoval = true)
private List<Comment> commentList = new ArrayList<>();
@Builder.Default
@OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
private List<PostImage> postImageList = new ArrayList<>();
@Builder.Default
@OneToMany(mappedBy = "post", cascade = CascadeType.REMOVE, orphanRemoval = true)
private List<PostLike> postLikeList = new ArrayList<>();
@Lob
private String content;
private String postType;
private int likeCnt;
private int commentCnt;
public void editPost(String content, String hashTag) {
this.content = content;
this.postType = hashTag;
}
public void increaseLikeCnt() {
this.likeCnt = likeCnt+1;
}
public void decreaseLikeCnt() {
this.likeCnt = likeCnt - 1;
}
}
複数のリストのペア(CommentList、PostImageList、PostLikeListなど)を表示すると、カスケード・オプションが表示されます.
ERD例
今回の位置決めで使用するエンティティおよび関連関係も、前の位置決めと同じです.
Post
package couch.camping.domain.post.entity;
import couch.camping.domain.base.BaseEntity;
import couch.camping.domain.comment.entity.Comment;
import couch.camping.domain.member.entity.Member;
import couch.camping.domain.postimage.entity.PostImage;
import couch.camping.domain.postlike.entity.PostLike;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Getter
public class Post extends BaseEntity {
@Id @GeneratedValue
@Column(name = "post_id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member member;
@Builder.Default
@OneToMany(mappedBy = "post", cascade = CascadeType.REMOVE, orphanRemoval = true)
private List<Comment> commentList = new ArrayList<>();
@Builder.Default
@OneToMany(mappedBy = "post", cascade = CascadeType.ALL, orphanRemoval = true)
private List<PostImage> postImageList = new ArrayList<>();
@Builder.Default
@OneToMany(mappedBy = "post", cascade = CascadeType.REMOVE, orphanRemoval = true)
private List<PostLike> postLikeList = new ArrayList<>();
@Lob
private String content;
private String postType;
private int likeCnt;
private int commentCnt;
public void editPost(String content, String hashTag) {
this.content = content;
this.postType = hashTag;
}
public void increaseLikeCnt() {
this.likeCnt = likeCnt+1;
}
public void decreaseLikeCnt() {
this.likeCnt = likeCnt - 1;
}
}
複数のリストのペア(CommentList、PostImageList、PostLikeListなど)を表示すると、カスケード・オプションが表示されます.PostLike
package couch.camping.domain.postlike.entity;
import couch.camping.domain.member.entity.Member;
import couch.camping.domain.post.entity.Post;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.*;
@Entity
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class PostLike {
@Id @GeneratedValue
@Column(name = "post_like_id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "post_id")
private Post post;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member member;
}
PostImage
package couch.camping.domain.postimage.entity;
import couch.camping.domain.base.BaseEntity;
import couch.camping.domain.member.entity.Member;
import couch.camping.domain.post.entity.Post;
import lombok.*;
import javax.persistence.*;
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Getter
public class PostImage extends BaseEntity {
@Id @GeneratedValue
@Column(name = "post_image_id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member member;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "post_id")
private Post post;
private String imgUrl;
public PostImage(Member member, Post post, String imgUrl) {
this.member = member;
this.post = post;
this.imgUrl = imgUrl;
}
}
Comment
package couch.camping.domain.comment.entity;
import couch.camping.domain.base.BaseEntity;
import couch.camping.domain.commentlike.entity.CommentLike;
import couch.camping.domain.member.entity.Member;
import couch.camping.domain.post.entity.Post;
import lombok.*;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Getter
public class Comment extends BaseEntity {
@Id @GeneratedValue
@Column(name = "comment_id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member member;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "post_id")
private Post post;
@Builder.Default
@OneToMany(mappedBy = "comment", cascade = CascadeType.REMOVE, orphanRemoval = true)
List<CommentLike> commentLikeList = new ArrayList<>();
@Lob
private String content;
private int likeCnt;
public void editComment(String content){
this.content = content;
}
public void increaseLikeCnt() {
this.likeCnt = likeCnt+1;
}
public void decreaseLikeCnt() {
this.likeCnt = likeCnt - 1;
}
}
コメントエンティティフィールドでコメントLikeList@OneToManyリストを表示すると、カスケードオプションが表示されます.CommentLike
package couch.camping.domain.commentlike.entity;
import couch.camping.domain.comment.entity.Comment;
import couch.camping.domain.member.entity.Member;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.*;
@Entity
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Getter
public class CommentLike {
@Id
@GeneratedValue
@Column(name = "comment_like_id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "comment_id")
private Comment comment;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "member_id")
private Member member;
}
保存後のエンティティ
postエンティティはpostImageと1対多の関連付けがあり、カスケードオプションはCascadeTypeである.上の確認はALLです.
では、postエンティティを永続化する場合、post画像を単独でem.save()する必要がありますか?
saveじゃない一度呼んでも大丈夫
例
Post post = Post.builder()
.title(postWriteRequestDto.getTitle())
.content(postWriteRequestDto.getContent())
.postType(postWriteRequestDto.getPostType())
.lastModifiedDate(LocalDateTime.now())
.member(member)
.build();
// Post 엔티티 postImageList 필듸에 add() 하는 로직
for (String imgUrl : postWriteRequestDto.getImgUrlList()) {
PostImage postImage = new PostImage(member, post, imgUrl);
post.getPostImageList().add(postImage);
}
Post savePost = postRepository.save(post);
上のコードはrequest DTOを受信しpost em.save()を行う論理です.ただし、postImageエンティティにem.save()を提供するコードは見つかりません.理由は.
CascadeオプションはCascadeTypeです.ALLなので、postエンティティのpost-ImageListに()を追加して最後にpostエンティティを保存すると、リスト内のpost-Imageも一緒に保存されます.(上記のコードコメントを参照)
検索
投稿に3枚の写真(postImage)を作成し、次のクエリで投稿を保存します.
これは、前述したように、1つのPostを保存する場合に、3つのPostImageを同時に保存するためである.insert
into
post
(comment_cnt, content, created_by, created_date, last_modified_date, like_cnt, member_id, post_type, title, post_id)
values
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
>>>>>>>>> 게시글 저장 쿼리
insert
into
post_image
(created_date, last_modified_date, created_by, last_modified_by, img_url, member_id, post_id, post_image_id)
values
(?, ?, ?, ?, ?, ?, ?, ?)
>>>>>>>> 첫번째 게시글 이미지 저장 쿼리
insert
into
post_image
(created_date, last_modified_date, created_by, last_modified_by, img_url, member_id, post_id, post_image_id)
values
(?, ?, ?, ?, ?, ?, ?, ?)
>>>>>>>>> 두번째 게시글 이미지 저장 쿼리
insert
into
post_image
(created_date, last_modified_date, created_by, last_modified_by, img_url, member_id, post_id, post_image_id)
values
(?, ?, ?, ?, ?, ?, ?, ?)
>>>>>>>>> 세번째 게시글 이미지 저장 쿼리
postエンティティの削除
重要!
これが今回の宣伝の核心だ.
ではpostエンティティを削除するとどうなるのでしょうか.
各cascadeオプションは(ALL、REMOVE、REMOVE)である.
すなわち、postを削除すると、チェーン削除するpostエンティティに関連付けられたすべてのエンティティが削除されます.
に注意
ではpostと直接連絡して、postImage、postLike、commentだけを削除しますか?
いいえ.
Commentエンティティフィールドをよく見ると、commentLikeListのcascade=CascadeTypeが表示されます.REMOVEオプションが設定されていることがわかります.
postエンティティを削除するときは、プロセスを見てみましょう.
Post post = Post.builder()
.title(postWriteRequestDto.getTitle())
.content(postWriteRequestDto.getContent())
.postType(postWriteRequestDto.getPostType())
.lastModifiedDate(LocalDateTime.now())
.member(member)
.build();
// Post 엔티티 postImageList 필듸에 add() 하는 로직
for (String imgUrl : postWriteRequestDto.getImgUrlList()) {
PostImage postImage = new PostImage(member, post, imgUrl);
post.getPostImageList().add(postImage);
}
Post savePost = postRepository.save(post);
insert
into
post
(comment_cnt, content, created_by, created_date, last_modified_date, like_cnt, member_id, post_type, title, post_id)
values
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
>>>>>>>>> 게시글 저장 쿼리
insert
into
post_image
(created_date, last_modified_date, created_by, last_modified_by, img_url, member_id, post_id, post_image_id)
values
(?, ?, ?, ?, ?, ?, ?, ?)
>>>>>>>> 첫번째 게시글 이미지 저장 쿼리
insert
into
post_image
(created_date, last_modified_date, created_by, last_modified_by, img_url, member_id, post_id, post_image_id)
values
(?, ?, ?, ?, ?, ?, ?, ?)
>>>>>>>>> 두번째 게시글 이미지 저장 쿼리
insert
into
post_image
(created_date, last_modified_date, created_by, last_modified_by, img_url, member_id, post_id, post_image_id)
values
(?, ?, ?, ?, ?, ?, ?, ?)
>>>>>>>>> 세번째 게시글 이미지 저장 쿼리
重要!
これが今回の宣伝の核心だ.
ではpostエンティティを削除するとどうなるのでしょうか.
各cascadeオプションは(ALL、REMOVE、REMOVE)である.
すなわち、postを削除すると、チェーン削除するpostエンティティに関連付けられたすべてのエンティティが削除されます.
に注意
ではpostと直接連絡して、postImage、postLike、commentだけを削除しますか?
いいえ.
Commentエンティティフィールドをよく見ると、commentLikeListのcascade=CascadeTypeが表示されます.REMOVEオプションが設定されていることがわかります.
postエンティティを削除するときは、プロセスを見てみましょう.
それはなぜですか。postではなくサブエンティティから削除しますか?
親エンティティを先に削除すると、子エンティティの親エンティティは参照できません.
したがって、サブエンティティのカスケードオプションをREMOVEまたはALLに設定すると、削除するエンティティの関連関係の末尾から削除が開始されます.
Reference
この問題について(スプリングJPAの双方向関連関係を理解する-永続的な移行カスケード), 我々は、より多くの情報をここで見つけました https://velog.io/@coconenne/스프링-JPA-양방향-연관관계의-이해-cascadeテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol