スプリング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など)を表示すると、カスケード・オプションが表示されます.
  • commentList
  • コメント(コメント)について、post(投稿)が削除された場合、カスケード=CascadeTypeは、postに関連付けられたコメントを連続的に削除します.REMOVEが設置されています.
  • postImageList
  • PostImage(パブリッシュ画像)では、作成、削除などのライフサイクルがパブリッシュコンテンツに依存するため、cascade=CascadeTypeとなる.ALLに設定します.
  • postLikeList
  • PostLike(良い投稿)についてpost(良い投稿)を削除した場合、postに関連するPostLikeをチェーン削除するためにcascade=CascadeTypeとなります.REMOVEが設置されています.
  • 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リストを表示すると、カスケードオプションが表示されます.
  • commentLikeList
  • アノテーション・ライブラリ(コメントが好き)エンティティの場合、アノテーション(コメント)エンティティを削除すると、カスケード=CascadeTypeとなり、アノテーションに関連するアノテーション・ライブラリが連続的に削除されます.REMOVEが設置されています.
  • 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エンティティを削除するときは、プロセスを見てみましょう.
  • 削除前関連付け(PostImage、PostLike、Comment)
  • postに関連付けられたコメントを削除する前に、コメントリンクエンティティ
  • を削除します.
  • 最終削除するPost削除
  • それはなぜですか。postではなくサブエンティティから削除しますか?


    親エンティティを先に削除すると、子エンティティの親エンティティは参照できません.
    したがって、サブエンティティのカスケードオプションをREMOVEまたはALLに設定すると、削除するエンティティの関連関係の末尾から削除が開始されます.