미해결
자바 ORM 표준 JPA 프로그래밍 - 기본편
em.find()와 영속성 컨텍스트 관련 질문
안녕하세요!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();
}저는 위 테스트에 대한 결과 예측을 아래와 같이 했습니다.insert문을 DB에 날린다이후 em.clear()로 영속성 컨텍스트를 비워주어 1차 캐시에는 데이터 존재 X======delete 메서드 호출한다. 1차 캐시에 데이터가 존재하지 않아 select 쿼리문 후 delete 쿼리문을 날려야 함. (SimpleJpaRepository) 이 쿼리문들은 쓰기지연 저장소에 저장돼서 아직 날라가지 않음.=======em.find()를 호출하고, 1차 캐시에 데이터가 존재하지 않아 select 쿼리문이 쓰기지연저장소에 쌓임.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