cascade option 적용 후 부모 객체 삭제시 쿼리의 수 질문
411
작성한 질문수 30
1.코드상황
Member(부모), Latter(자식)의 양방향 연관관계로 매핑되어있고 Member 1개에 Latter2개가 저장되어 있는데 이를 지우는 과정에서의 쿼리 개수가 예상과 다른 상황입니다.
"delete from latter where latter_member_id = ?" 하나가 나와 다 지울 줄 알았으나
"delete from latter where latter_id = ?"
"delete from latter where latter_id = ?"
총 2번 쿼리가 나갑니다.
2.코드
Latter.class
package dev.devpool.domain;
import dev.devpool.domain.enums.IsCheck;
import javax.persistence.*;
import static javax.persistence.EnumType.STRING;
@Entity
public class Latter {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "LATTER_ID")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "MEMBER_ID")
private Member member;
private String body;
@Enumerated(STRING)
private IsCheck isCheck;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Member getMember() {
return member;
}
public void setMember(Member member) {
this.member = member;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public IsCheck getIsCheck() {
return isCheck;
}
public void setIsCheck(IsCheck isCheck) {
this.isCheck = isCheck;
}
}
Member.class
package dev.devpool.domain;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
public class Member {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "MEMBER_ID")
private Long id;
private String name;
private String nickName;
private String email;
private String password;
public Member() {
}
public Member(String name, String nickName, String email, String password) {
this.name = name;
this.nickName = nickName;
this.email = email;
this.password = password;
}
@OneToMany(mappedBy = "member", cascade = CascadeType.ALL)
private List<Certificate> certificates = new ArrayList<>();
@OneToMany(mappedBy = "member", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Latter> latters = new ArrayList<>();
public List<Latter> getLatters() {
return latters;
}
public void setLatters(List<Latter> latters) {
this.latters = latters;
}
public void setId(Long id) {
this.id = id;
}
public List<Certificate> getCertificates() {
return certificates;
}
public void setCertificates(List<Certificate> certificates) {
this.certificates = certificates;
}
public Long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
/**
* 편의 메서드
*/
public void addCertificate(Certificate certificate) {
certificate.setMember(this);
certificates.add(certificate);
}
public void addLatter(Latter latter) {
latter.setMember(this);
latters.add(latter);
}
}
MemberService의 deleteById
public void deleteById(long memberId) {
Member findMember = em.find(Member.class, memberId);
if (findMember != null) {
em.remove(findMember);
}
}
Test코드
@Test
public void 쪽지삭제ByMember() {
transactionTemplate.execute(status -> {
//given
Member member = new Member();
Latter latter1 = new Latter();
Latter latter2 = new Latter();
member.addLatter(latter1);
member.addLatter(latter2);
memberService.join(member);
em.flush();
em.clear();
//when
memberService.deleteById(member.getId());
em.flush();
em.clear();
//then
List<Latter> latters = latterService.findAllByMemberId(member.getId());
assertEquals(0, latters.size());
return null;
});
}
쿼리 결과
Hibernate:
select
member0_.member_id as member_i1_2_,
member0_.email as email2_2_,
member0_.name as name3_2_,
member0_.nick_name as nick_nam4_2_,
member0_.password as password5_2_
from
member member0_
where
member0_.email=?
Hibernate:
insert
into
member
(member_id, email, name, nick_name, password)
values
(default, ?, ?, ?, ?)
Hibernate:
insert
into
latter
(latter_id, body, is_check, member_id)
values
(default, ?, ?, ?)
Hibernate:
insert
into
latter
(latter_id, body, is_check, member_id)
values
(default, ?, ?, ?)
Hibernate:
select
member0_.member_id as member_i1_2_0_,
member0_.email as email2_2_0_,
member0_.name as name3_2_0_,
member0_.nick_name as nick_nam4_2_0_,
member0_.password as password5_2_0_
from
member member0_
where
member0_.member_id=?
Hibernate:
***select
certificat0_.member_id as member_i3_0_0_,
certificat0_.certificate_id as certific1_0_0_,
certificat0_.certificate_id as certific1_0_1_,
certificat0_.body as body2_0_1_,
certificat0_.member_id as member_i3_0_1_
from
certificate certificat0_
where
certificat0_.member_id=?
Hibernate:
select
latters0_.member_id as member_i4_1_0_,
latters0_.latter_id as latter_i1_1_0_,
latters0_.latter_id as latter_i1_1_1_,
latters0_.body as body2_1_1_,
latters0_.is_check as is_check3_1_1_,
latters0_.member_id as member_i4_1_1_
from
latter latters0_
where
latters0_.member_id=?
***
***
Hibernate:
delete
from
latter
where
latter_id=?
Hibernate:
delete
from
latter
where
latter_id=?
***
Hibernate:
delete
from
member
where
member_id=?
Hibernate:
select
latter0_.latter_id as latter_i1_1_,
latter0_.body as body2_1_,
latter0_.is_check as is_check3_1_,
latter0_.member_id as member_i4_1_
from
latter latter0_
where
latter0_.member_id=?
3.질문
Q1) *** *** 부분에서 cascade 및 양방향으로 걸려있는 것들에 대해 다시 select 쿼리가 날라가는 이유가 무엇인가요?
Q2) 왜 latter_id를 바탕으로 쿼리가 2개 나가나요?
답변 1
0
안녕하세요. taeu kim님, 공식 서포터즈 David입니다.
Member를 삭제할 때 Cascade가 걸려있는 연관관계의 경우 연관된 것들을 조회한 뒤, 연관된 것들을 삭제하고 나서 Member를 삭제하게 됩니다. 이 과정에서 연관된 것들을 조회하기 위해 select가 나가게 됩니다.
em.remove()가 1건씩 삭제되도록 구현되어 있기 때문입니다. 이를 원치 않으신다면 삭제 쿼리를 직접 작성하셔서 사용하셔야 합니다. 아래 글을 참고해 주세요.
감사합니다.
벌크연산에서 member.getAge 호출 시 영속성 컨텍스트에서 데이터를 가져오는건가요?
0
28
2
inheritance startegy 선택시 고려사항
0
22
1
Entity 동등성 비교
0
21
1
실무 조언 관련 질문입니다.
0
47
1
H2데이터베이스 파일 생성
0
56
2
서브쿼리 강의에서 ALL 예시 관련 질문드립니다.
0
53
2
수정또는 삭제시 영속성 엔티티에 값이 무조건 있어야 하나요?
0
52
1
JPQL 메소드와 락
0
55
1
Delivery @OneToOne
0
60
1
17강 4~5분대 테이블 값 조회가 안됩니다.
0
94
2
UnsupportedOperationException 발생
0
86
3
H2 Database 연결이 안됩니다.
0
95
2
연관관계 매핑 질문드립니다.
0
85
2
h2데이터베이스 실행오류
0
108
2
persistence.xml
0
108
2
양방향 연관관계에서 연관관계의 주인(mappedBy)을 왜 꼭 정해야 하나요?
0
80
1
영속성 컨텍스트
0
66
1
JPA 프록시
0
96
1
Native Query와 MyBatis
0
70
1
영속성 컨텍스트는 어떤 메모리에 저장되는건가요?
0
87
1
임베디드 타입 예시 코드 관련 질문
0
115
3
명시적 조인에서 별칭을 주면 왜 객체에 접근할 수 있나요
0
95
3
인텔리제이 패키지 커서 단축키 질문
0
108
2
혹시 현재는 ID 데이터 타입이 String이면 안되나요?
0
145
1





