Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- batch insert
- 이펙티브 자바
- @Transaction(readOnly=true)
- ngrinder
- 동시성
- fetch join
- 데드락
- spring-cloud-starter-aws
- Batch
- mockito
- oauth2.0
- JPA
- jdbc
- Git
- @controller
- N + 1
- awspring
- @RequestMapping
- OIDC
- Cache
- Cannotacquirelockexception
- AWS
- spring
- 정적 팩터리 메서드
- MySQLTransactionRollbackException
- Hibernate
- 성능테스트
- injellij
- Convention
- assert
Archives
- Today
- Total
정리정리
[JPA] CascadeType.DELETE, orphanRemoval = true 차이 본문
엔티티 정의
우선 맴버와 게시글이 일대다 양방향 관계에 있다고 가정을 하겠습니다.
@Getter
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToMany(mappedBy = "member")
private List<Post> posts = new ArrayList<>();
}
@Getter
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name = "member_id")
private Member member;
public Post(Member member) {
this.member = member;
}
}
부모 엔티티 제거의 경우
우선 결과부터 말하자면 부모 엔티티를 제거할 경우, CascadeType.DELETE 와 orphanRemoval = true 둘 다 자식 엔티티가 제거가 됩니다.
CascadeType.DELETE
//Member.java
@OneToMany(mappedBy = "member", cascade = {CascadeType.PERSIST, CascadeType.DELETE})
private List<Post> posts = new ArrayList<>();
@Test
@DisplayName("CascadeType.DELETE인 경우, 부모 엔티티(Member)가 제거되면 자식 엔티티(Post)도 제거되어야 함")
void test() throws Exception {
//given
Member member = new Member();
Post post = new Post(member);
member.addPost(post);
member = memberRepository.save(member);
//when
memberRepository.delete(member);
//then
List<Post> posts = postRepository.findAll();
List<Member> members = memberRepository.findAll();
assertThat(posts).hasSize(0);
assertThat(members).hasSize(0);
}
orphanRemoval = true
//Member.java
@OneToMany(mappedBy = "member", cascade = CascadeType.PERSIST, orphanRemoval = true)
private List<Post> posts = new ArrayList<>();
@Test
@DisplayName("orphanRemoval = true 인 경우, 부모 엔티티(Member)가 제거되면 자식 엔티티(Post)도 제거되어야 함")
void test() throws Exception {
//given
Member member = new Member();
Post post = new Post(member);
member.addPost(post);
member = memberRepository.save(member);
//when
memberRepository.delete(member);
//then
List<Post> posts = postRepository.findAll();
List<Member> members = memberRepository.findAll();
assertThat(posts).hasSize(0);
assertThat(members).hasSize(0);
}
부모 엔티티에서 자식 엔티티 참조를 지울 경우
이 경우에서 두 옵션이 차이를 가집니다. 우선 테스트 코드부터 확인해 보겠습니다.
CascadeType.DELETE
//Member.java
@OneToMany(mappedBy = "member", cascade = {CascadeType.PERSIST, CascadeType.DELETE})
private List<Post> posts = new ArrayList<>();
@Test
@DisplayName("CascadeType.DELETE인 경우, 부모 엔티티(Member)와 관계가 끊겨도 자식 엔티티(Post)는 제거되지 않음")
void test() throws Exception {
//given
Member member = new Member();
Post post = new Post(member);
member.addPost(post);
member = memberRepository.save(member);
//when
member.getPosts().remove(0);
//then
List<Post> posts = postRepository.findAll();
List<Member> members = memberRepository.findAll();
assertThat(posts).hasSize(1); // post size가 1이어야 함
assertThat(members).hasSize(1);
}
Hibernate:
insert
into
member
(id)
values
(default)
Hibernate:
insert
into
post
(id, member_id)
values
(default, ?)
Hibernate:
select
post0_.id as id1_2_,
post0_.member_id as member_i2_2_
from
post post0_
Hibernate:
select
member0_.id as id1_1_
from
member member0_
쿼리 결과를 보시면 맨 처음 Member와 Post 등록 insert 쿼리, 마지막 테스트 검증을 위한 select 쿼리 외에 어떤 쿼리도 나가지 않았습니다.
orphanRemoval = true
//Member.java
@OneToMany(mappedBy = "member", cascade = CascadeType.PERSIST, orphanRemoval = true)
private List<Post> posts = new ArrayList<>();
@Test
@DisplayName("orphanRemoval = true 인 경우, 부모 엔티티(Member)와 관계가 끊기면 자식 엔티티(Post)는 제거되어야 함")
void test() throws Exception {
//given
Member member = new Member();
Post post = new Post(member);
member.addPost(post);
member = memberRepository.save(member);
//when
member.getPosts().remove(0);
//then
List<Post> posts = postRepository.findAll();
List<Member> members = memberRepository.findAll();
assertThat(posts).hasSize(0); // post size가 0이어야 함
assertThat(members).hasSize(1);
}
Hibernate:
insert
into
member
(id)
values
(default)
Hibernate:
insert
into
post
(id, member_id)
values
(default, ?)
Hibernate:
delete
from
post
where
id=?
Hibernate:
select
post0_.id as id1_2_,
post0_.member_id as member_i2_2_
from
post post0_
Hibernate:
select
member0_.id as id1_1_
from
member member0_
CascadeType.DELETE와는 다르게 전체 쿼리에서 게시글을 지우는 delete 쿼리가 중간에 있는 것을 볼 수 있습니다.
정리
orphanRemoval = true는 고아 객체를 지우는 옵션입니다. 그렇기 때문에 첫 번째 테스트였던 부모 엔티티(Member)가 아예 삭제가 되면 당연히 자식 엔티티(Post)는 고아가 되고, 해당 옵션의 기능이 수행된 것입니다.
반면에 CascadeType.DELETE 옵션은 해당 엔티티가 em.remove() 같이 실제 엔티티가 삭제가 될 때 그 옵션이 붙은 엔티티에 삭제라는 영속성이 전이가 된 것이기 때문에 단순히 부모 엔티티에서 자식 엔티티의 참조를 제거하는 것으로는 엔티티가 삭제가 되지 않습니다.
부모 엔티티 제거 | 부모 엔티티에서 자식 엔티티 참조 제거 | |
CascadeType.DELETE | 자식 제거됨 | 자식 제거 안 됨 |
orphanRemoval = true | 자식 제거됨 | 자식 제거됨 |
참고
'JPA' 카테고리의 다른 글
@Transactional(readOnly = true) 성능 향상되는 이유 (0) | 2023.05.01 |
---|---|
JPA N + 1 문제 (0) | 2023.04.11 |
Spring JPA 데이터베이스 초기화 (0) | 2023.03.27 |
JPA, Hibernate, Spring Data JPA, JDBC (0) | 2023.03.27 |
Comments