[PJT/JPAを選択]JPAの外部キー整合性/CAdidate Entityが双方向マッピングを放棄した理由


1.双方向マッピングを試みる理由

  • Candidateを削除する場合は、関連するsnsとyoutubeをCascadeで簡単に削除したいと思います.
  • Total like、Total commentsなどの統計機能を導入すると便利です.
  • 2.双方向マッピングを放棄した理由


    1つ目のアイデア:idクエリを使用して削除するCandidateを検索する場合は、関連エンティティ(sns、youtube)をfetchで結合する必要があります.cascadeがうまく適用されるのではないでしょうか.
    だからjoin fetchクエリーを書いたとき、金英漢の基礎授業で聞いた内容を思い出した.
  • の2つ以上の集合は
  • に結合できない.
  • @ToMany Patch Join改ページ不可
  • 以上のコレクションを貼り合わせると、1:N:Nマッピングが発生し、資料が話にならないように爆発する危険性があり、一貫性の問題が発生する可能性があります.
  • などの理由で、@ToManyページング結合はページングを制限し、ハードに使用するとメモリにページングされるため、非常に危険です.
  • Totallike、Totalcommentsなどの統計機能はSNS、Youtube表を一緒にチェックして結果を出す必要があります...授業の内容によって.
    複数のテーブル間を結合してエンティティの形状とまったく異なる結果を生成する必要がある場合は、ペアリング結合ではなく通常の結合を使用し、必要なデータのみを問合せてDTOに戻るのが有効です.
    したがって,双方向マッピングを再利用する理由はない.

    3.参照整合性の確保方法


    現在のSns、YoutubeエンティティはCandidateに関連付けられているため、Spring Bootを起動すると次のクエリが失われます.

    CandidateはCascadeとして管理されなくなったため、レポート候補となります.remove(候補)を試してみると、次のエラーが発生します.
    org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint ["FKDGYKRP9VG5PBS7S05I53938T2: PUBLIC.SNS FOREIGN KEY(CANDIDATE_ID) REFERENCES PUBLIC.CANDIDATE(CANDIDATE_ID) (2)"; SQL statement:
    delete from candidate where candidate_id=? [23503-200]]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
    これは、外部キーの整合性の要件に違反しているため、クエリ自体が実行できないことを意味します.当たり前のことです.スプリングは外部キーの整合性を管理しないためです.
    したがって、この場合、データベースは外部キーの整合性を管理するために設定を変更する必要があります.
    SQLはデータベースをON DELETE CASCADEに設定しますが、Spring Bootを使用してテーブルを作成するため、SnsおよびYoutubeエンティティに次の項目を追加する必要があります.
    @OnDelete(action = OnDeleteAction.CASCADE)
    public class Youtube {
    
        @Id @GeneratedValue
        @Column(name = "youtube_id")
        private Long id;
    
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "candidate_id")
        @OnDelete(action = OnDeleteAction.CASCADE) //여기에 추가해 주시면 됩니다.
        private Candidate candidate;
        
        ...
    }
    そうすると、alterクエリにondeleteカスケードが追加されます.

    @Inheritance(strateg=InheritanceType.JOINED)に設定されているエンティティの場合:
    @Entity
    @DiscriminatorValue("F")
    @OnDelete(action = OnDeleteAction.CASCADE)
    @NoArgsConstructor(access = AccessLevel.PROTECTED)
    @Getter
    public class Facebook extends Sns{
    
        private int likes;
        private int comments;
        private int shares;
    これでいい

    4.変更されたコード


    Candidate Entity

    @Entity
    @Getter
    @NoArgsConstructor(access = AccessLevel.PROTECTED)
    public class Candidate {
    
        @Id @GeneratedValue
        @Column(name = "candidate_id")
        private Long id;
    
        private int number;
    
        @Column(name = "candidate_name")
        private String name;
    
        @Column(name = "candidate_likes")
        private int likes;
    
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "city_id")
        private City city;
    
        @Builder
        public Candidate(int number, String name, City city) {
            this.number = number;
            this.name = name;
            this.likes = 0;
            this.city = city;
        }
    }
    削除
  • 関連メソッド
  • Candidate Service


    5.Candidateエンティティを変更してテストする


    テストコード

    @Test
        public void 후보_삭제_ON_DELETE_CASCADE() throws Exception {
            //given
            City city = createCity();
            Candidate candidate = new Candidate(1, "Jake", city);
            candidateRepository.save(candidate);
            Facebook facebook = new Facebook(candidate, "content1", "url", LocalDateTime.now(), 1, 1, 1);
            facebookRepository.save(facebook);
            Youtube youtube = new Youtube(candidate, "url", "title", "thumbnail", LocalDateTime.now(), "description", 1, 1, LocalDateTime.now());
            youtubeRepository.save(youtube);
    
            //when
            Long candidateId = candidate.getId();
            candidateService.delete(candidateId);
    
            //then
            assertEquals(0, snsRepository.findAllByCandidateId(candidateId).size());
            assertEquals(0, youtubeRepository.findAllByCandidateId(candidateId).size());
    
        }

    テスト結果