작성
·
358
0
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건씩 삭제되도록 구현되어 있기 때문입니다. 이를 원치 않으신다면 삭제 쿼리를 직접 작성하셔서 사용하셔야 합니다. 아래 글을 참고해 주세요.
감사합니다.