cascade option 적용 후 부모 객체 삭제시 쿼리의 수 질문
428
작성한 질문수 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건씩 삭제되도록 구현되어 있기 때문입니다. 이를 원치 않으신다면 삭제 쿼리를 직접 작성하셔서 사용하셔야 합니다. 아래 글을 참고해 주세요.
감사합니다.
리뉴얼 시기
0
53
2
변경감지(더티체킹) 이후 플러시할 경우의 쿼리
0
73
1
단일 테이블 시 null 값과 연관관계 필요성 질문
0
65
1
벌크연산에서 member.getAge 호출 시 영속성 컨텍스트에서 데이터를 가져오는건가요?
0
95
2
inheritance startegy 선택시 고려사항
0
62
1
Entity 동등성 비교
0
66
1
실무 조언 관련 질문입니다.
0
91
1
H2데이터베이스 파일 생성
0
105
2
서브쿼리 강의에서 ALL 예시 관련 질문드립니다.
1
95
2
수정또는 삭제시 영속성 엔티티에 값이 무조건 있어야 하나요?
0
85
1
JPQL 메소드와 락
0
81
1
Delivery @OneToOne
0
99
1
17강 4~5분대 테이블 값 조회가 안됩니다.
0
143
2
UnsupportedOperationException 발생
0
162
3
H2 Database 연결이 안됩니다.
0
128
2
연관관계 매핑 질문드립니다.
0
128
2
h2데이터베이스 실행오류
0
144
2
persistence.xml
0
154
2
양방향 연관관계에서 연관관계의 주인(mappedBy)을 왜 꼭 정해야 하나요?
0
122
1
영속성 컨텍스트
0
102
1
JPA 프록시
0
134
1
Native Query와 MyBatis
0
112
1
영속성 컨텍스트는 어떤 메모리에 저장되는건가요?
0
123
1
임베디드 타입 예시 코드 관련 질문
0
166
3





