カスケードを使用する際の注意点
持続性変換をプロジェクトに適用する際、多くの紆余曲折を経験しましたが、ここで学んだ内容をまとめたいと思います.
この投稿で使用されるエンティティのコードは次のとおりです.
Entity Manager=em.
候補エンティティの@OneToMany(mappedBy="候補",cascade=CascadeType.ALL)宣言のため,候補エンティティが保持されている場合にはFacebookも構造である.
Entity ManagerがFacebookを削除した以上、テストは成功するでしょう?!
成功した!
上のコードにem.flush()を追加します.
ドラゴン?!これは同じテストコードで、リフレッシュ()だけで失敗したテストになります.
クエリーに注目する必要がありますが、insertクエリーのみ、deleteクエリーは飛んでいません.
理由はCascadeTypeall, CascadeType.persistに設定されているためです.
EntityManagerは、更新()とコミット()の間、永続的な管理状態にあるエンティティに対してクエリーを発行します.このときEntityManagerには候補エンティティがあり、SnListにはCascadeTypeがあります.これは、すべての操作が永続的なコンテキストでFacebookに登録され、insertクエリに戻ることを意味します.
候補のsnsListからFacebookオブジェクトを削除することもできます.永続コンテキストからFacebookオブジェクトを削除します. 候補のsnsListからFacebookオブジェクトを削除すると クエリが em.flush()から終了すると、変更+CascadeTypeが検出されます.allは、永続性コンテキストでFacebookオブジェクトを完全に切断し、deleteクエリーを実行します.
孤立Removal=true属性を候補の@OneToMany宣言に入れます.
この投稿で使用されるエンティティのコードは次のとおりです.
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;
@OneToMany(mappedBy = "candidate", cascade = CascadeType.ALL)
private List<Sns> snsList = new ArrayList<>();
@OneToMany(mappedBy = "candidate", cascade = CascadeType.ALL)
private List<Youtube> youtubeList = new ArrayList<>();
@Builder
public Candidate(int number, String name, City city) {
this.number = number;
this.name = name;
this.likes = 0;
this.city = city;
}
//== 연관관계 편의 메서드 ==//
public void addSns(Sns sns) {
snsList.add(sns);
sns.setCandidate(this);
}
}
Sns Entity
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "dtype")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class Sns {
@Id @GeneratedValue
@Column(name = "sns_id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "candidate_id")
private Candidate candidate;
@Column(columnDefinition = "Text")
private String content;
private String url;
private LocalDateTime uploadDate;
public Sns(String content, String url, LocalDateTime uploadDate) {
this.content = content;
this.url = url;
this.uploadDate = uploadDate;
}
public void setCandidate(Candidate candidate) {
this.candidate = candidate;
}
}
Facebook Entity
@Entity
@DiscriminatorValue("F")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class Facebook extends Sns{
private int likes;
private int comments;
private int shares;
@Builder
public Facebook(String content, String url, LocalDateTime uploadDate, int likes, int comments, int shares) {
super(content, url, uploadDate);
this.likes = likes;
this.comments = comments;
this.shares = shares;
}
public void change(int likes, int comments, int shares) {
this.likes = likes;
this.comments = comments;
this.shares = shares;
}
}
あなたのエンティティを実行すると、remove()も残るのではないでしょうか。
Entity Manager=em.
@Test
public void 영속성전이_remove() throws Exception {
//given
Candidate candidate = createCandidate();
Facebook facebook = new Facebook("content1", "url", LocalDateTime.now(), 1, 1, 1);
candidate.addSns(facebook);
candidateRepository.save(candidate);
//when
facebookRepository.remove(facebook); //em.remove(facebook); 과 같은 동작입니다.
//then
Assertions.assertTrue(em.contains(facebook));
}
候補エンティティの@OneToMany(mappedBy="候補",cascade=CascadeType.ALL)宣言のため,候補エンティティが保持されている場合にはFacebookも構造である.
Entity ManagerがFacebookを削除した以上、テストは成功するでしょう?!
成功した!
でもクエリーに失敗したら...?
上のコードにem.flush()を追加します.
@Test
public void 영속성전이_remove() throws Exception {
//given
Candidate candidate = createCandidate();
Facebook facebook = new Facebook("content1", "url", LocalDateTime.now(), 1, 1, 1);
candidate.addSns(facebook);
candidateRepository.save(candidate);
//when
facebookRepository.remove(facebook);
em.flush();
//then
Assertions.assertTrue(em.contains(facebook));
}
結果
ドラゴン?!これは同じテストコードで、リフレッシュ()だけで失敗したテストになります.
クエリーに注目する必要がありますが、insertクエリーのみ、deleteクエリーは飛んでいません.
n/a.理由
理由はCascadeTypeall, CascadeType.persistに設定されているためです.
EntityManagerは、更新()とコミット()の間、永続的な管理状態にあるエンティティに対してクエリーを発行します.
解決策
候補のsnsListからFacebookオブジェクトを削除することもできます.
@Test
public void 영속성전이_remove() throws Exception {
//given
Candidate candidate = createCandidate();
Facebook facebook = new Facebook("content1", "url", LocalDateTime.now(), 1, 1, 1);
candidate.addSns(facebook);
candidateRepository.save(candidate);
//when
em.remove(facebook);
candidate.getSnsList().remove(0);//이 부분입니다
em.flush();
//then
Assertions.assertFalse(em.contains(facebook));
}
解決策
孤立Removal=true属性を候補の@OneToMany宣言に入れます.
@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;
@OneToMany(mappedBy = "candidate", cascade = CascadeType.ALL, orphanRemoval = true) //바뀐 부분입니다.
private List<Sns> snsList = new ArrayList<>();
@OneToMany(mappedBy = "candidate", cascade = CascadeType.ALL)
private List<Youtube> youtubeList = new ArrayList<>();
@Builder
public Candidate(int number, String name, City city) {
this.number = number;
this.name = name;
this.likes = 0;
this.city = city;
}
//== 연관관계 편의 메서드 ==//
public void addSns(Sns sns) {
snsList.add(sns);
sns.setCandidate(this);
}
}
Reference
この問題について(カスケードを使用する際の注意点), 我々は、より多くの情報をここで見つけました https://velog.io/@mincho920/영속성-전이cascade를-사용할-때-주의해야-할-점テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol