인프런 커뮤니티 질문&답변

taeu kim님의 프로필 이미지
taeu kim

작성한 질문수

자바 ORM 표준 JPA 프로그래밍 - 기본편

영속성 전이(CASCADE)와 고아 객체

cascade option 적용 후 부모 객체 삭제시 쿼리의 수 질문

작성

·

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입니다.

  1. Member를 삭제할 때 Cascade가 걸려있는 연관관계의 경우 연관된 것들을 조회한 뒤, 연관된 것들을 삭제하고 나서 Member를 삭제하게 됩니다. 이 과정에서 연관된 것들을 조회하기 위해 select가 나가게 됩니다.

  2. em.remove()가 1건씩 삭제되도록 구현되어 있기 때문입니다. 이를 원치 않으신다면 삭제 쿼리를 직접 작성하셔서 사용하셔야 합니다. 아래 글을 참고해 주세요.

    https://jojoldu.tistory.com/235

감사합니다.

taeu kim님의 프로필 이미지
taeu kim

작성한 질문수

질문하기