inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

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

em.find()와 영속성 컨텍스트 관련 질문

1052

TaeHyeon Kim

작성한 질문수 18

1

안녕하세요!

em.find()와 영속성 컨텍스트 관련해서 학습테스트를 작성하던 도중 의문이 생겨 질문을 남깁니다.

em.find() 작업은 1차 캐시에 데이터가 존재한다면 영속성컨텍스트의 데이터를 반환하고, 1차 캐시에 데이터가 존재하지 않을 경우 DB에 select 쿼리문을 보내는 것으로 알고 있습니다.

 

위 선행지식을 바탕으로, 아래의 학습테스트 경우처럼 1차 캐시가 비어있고 DB에 데이터가 있는 경우에서 delete 메서드를 호출하고 EntityManager를 flush()하지 않은 상태로 테스트하여 delete 쿼리문을 DB에 보내지 않고 쓰기지연 저장소에 남겨둔 상태를 테스트해보도록 했습니다.

(여기서 delete는 Spring Data JPA의 delete문입니다! 이 delete 메서드에선 해당 JpaRepository의 구현체를 타겟 프록시로 하는 SimpleJpaRepository에서 em.find() 작업 후 em.remove() 작업을 해주는 것으로 알고 있습니다.)

학습 테스트


    @Test
    @DisplayName("1차 캐시가 비어있고 쓰기지연 저장소에 delete 쿼리가 있는 상태에서 em.find()를 할 경우 결과를 확인한다")
    void test7() {
        // 영속성 컨텍스트, DB에 모두 member 저장 (IDENTITY 전략)
        final Member member = new Member("kth990303", "kth990303@naepyeon.com", Platform.KAKAO, "1");
        final Long memberId = memberRepository.save(member)
                .getId();

        // 영속성 컨텍스트는 비워줌
        em.flush();
        em.clear();

        System.out.println("===========================");
        // delete 쿼리는 쓰기지연저장소에 존재하고 아직 sql로 찌르지는 않음
        memberRepository.delete(member);

        System.out.println("=============================================");
        // 1차캐시에 Member는 존재하지 않으므로 select 쿼리가 이 때 나갈 줄 알았으나 안나감. 
        em.find(Member.class, memberId);
        em.flush();
        em.clear();
    }

저는 위 테스트에 대한 결과 예측을 아래와 같이 했습니다.

  1. insert문을 DB에 날린다

  2. 이후 em.clear()로 영속성 컨텍스트를 비워주어 1차 캐시에는 데이터 존재 X

  3. ======

  4. delete 메서드 호출한다. 1차 캐시에 데이터가 존재하지 않아 select 쿼리문 후 delete 쿼리문을 날려야 함. (SimpleJpaRepository) 이 쿼리문들은 쓰기지연 저장소에 저장돼서 아직 날라가지 않음.

  5. =======

  6. em.find()를 호출하고, 1차 캐시에 데이터가 존재하지 않아 select 쿼리문이 쓰기지연저장소에 쌓임.

  7. em.flush()를 해주어 쓰기지연저장소에 있던 select, delete, select가 나갈 것이라 예측.

결과적으로 insert -> ==== -> ===== -> select, delete, select 가 나갈 것이라 생각했습니다.

하지만 실제 쿼리는 아래와 같았습니다.

실제로는 위와 같이 insert -> ==== -> select -> ===== -> delete 만 나가게 됐습니다.

이에 대한 이유가 궁금합니다!

감사합니다.

 

 

참고1. SimpleJpaRepository의 delete 메서드

@Override
@Transactional
@SuppressWarnings("unchecked")
public void delete(T entity) {

	Assert.notNull(entity, "Entity must not be null!");

	if (entityInformation.isNew(entity)) {
		return;
	}

	Class<?> type = ProxyUtils.getUserClass(entity);

	T existing = (T) em.find(type, entityInformation.getId(entity));

	// if the entity to be deleted doesn't exist, delete is a NOOP
	if (existing == null) {
		return;
	}

	em.remove(em.contains(entity) ? entity : em.merge(entity));
}

참고2. 해당 질문에 대한 상황을 정리한 노션

https://clean-nutria-44b.notion.site/JPA-em-find-1-34fa1ba3df914e24ba9dd9a143f28c8c

jpa java persistencecontext JPA

답변 2

0

TaeHyeon Kim

답변 감사합니다.

하지만 아직 제가 부족한 탓에 잘 이해가 되지 않습니다 ㅜㅜ

remove의 경우도 flush() 호출 전까지는 영속성 컨텍스트에 존재하기 때문에, delete() 코드 다음 flush()를 해주지 않으면 =====, ======== 사이에서는 아무 쿼리문도 보내지 않을 줄 알았는데, 실제로는 =====, ======== 사이에 select 쿼리문을 보내는 이유가 궁금합니다!

 

영한님 답변대로 delete() 다음 em.flush()를 하면 insert -> select, delete -> select로 의도한대로 잘 나가더라구요! 근데 delete() 이후 즉시 flush()를 해주지 않으면(즉, 위의 질문한 코드면) 아래와 같은 의문점이 드는 부분이 존재합니다.

 

  1. =====, ======== 사이에서 왜 select 쿼리가 나가는가? 이 사이에선 아무것도 쿼리가 나가지 않아야 하는 게 맞지 않을까?

  2. 왜 맨 마지막 em.find()의 select 쿼리는 나가지 않는걸까?

 

추가로 답변해주신다면 정말 감사하겠습니다.

좋은 하루 되세요 :)

0

김영한

안녕하세요. TaeHyeon Kim님

remove의 경우도 flush()를 호출하기 전까지는 영속성 컨텍스트에 존재하게 됩니다.

delete() 코드 다음에 em.flush()를 넣어보시면 이해가 되실꺼에요.

감사합니다.

벌크연산에서 member.getAge 호출 시 영속성 컨텍스트에서 데이터를 가져오는건가요?

0

5

2

inheritance startegy 선택시 고려사항

0

18

1

Entity 동등성 비교

0

17

1

실무 조언 관련 질문입니다.

0

44

1

H2데이터베이스 파일 생성

0

55

2

서브쿼리 강의에서 ALL 예시 관련 질문드립니다.

0

52

2

수정또는 삭제시 영속성 엔티티에 값이 무조건 있어야 하나요?

0

51

1

JPQL 메소드와 락

0

55

1

Delivery @OneToOne

0

60

1

17강 4~5분대 테이블 값 조회가 안됩니다.

0

91

2

UnsupportedOperationException 발생

0

85

3

H2 Database 연결이 안됩니다.

0

92

2

연관관계 매핑 질문드립니다.

0

84

2

h2데이터베이스 실행오류

0

107

2

persistence.xml

0

106

2

양방향 연관관계에서 연관관계의 주인(mappedBy)을 왜 꼭 정해야 하나요?

0

79

1

영속성 컨텍스트

0

64

1

JPA 프록시

0

93

1

Native Query와 MyBatis

0

66

1

영속성 컨텍스트는 어떤 메모리에 저장되는건가요?

0

85

1

임베디드 타입 예시 코드 관련 질문

0

114

3

명시적 조인에서 별칭을 주면 왜 객체에 접근할 수 있나요

0

93

3

인텔리제이 패키지 커서 단축키 질문

0

108

2

혹시 현재는 ID 데이터 타입이 String이면 안되나요?

0

142

1