JPA의 em.find 성질

@Test
@DisplayName("영속성 컨텍스트가 비어있으면 em.find는 DB에도 찔러본다")
void test1() {
    // 영속성 컨텍스트, DB에 모두 member 저장 (IDENTITY 전략)
    final Member member = new Member("kth990303", "[email protected]", Platform.KAKAO, "1");
    final Long memberId = memberRepository.save(member)
            .getId();

    // 영속성 컨텍스트 비워줌
    em.flush();
    em.clear();
    System.out.println("============================================");

    em.find(Member.class, memberId);
}

Untitled

JPA의 em.remove와 준영속 상태

@Test
@DisplayName("영속성 컨텍스트가 비어있으면 em.remove는 예외를 발생시킨다 -> 준영속 상태 엔티티에 접근하므로")
void test2() {
    // 영속성 컨텍스트, DB에 모두 member 저장 (IDENTITY 전략)
    final Member member = new Member("kth990303", "[email protected]", Platform.KAKAO, "1");
    memberRepository.save(member);

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

    System.out.println("===========================");
    assertThatThrownBy(() -> em.remove(member));
}

Untitled

JPA의 delete문은 1차 캐시가 비어있을 때에 delete 쿼리 전에 select 쿼리를 발생시킨다.

@Test
@DisplayName("em.remove는 1차 캐시가 비어있을 때 delete 쿼리 뿐만 아니라 select 쿼리도 발생시킨다")
void test3() {
    // 영속성 컨텍스트, DB에 모두 member 저장 (IDENTITY 전략)
    final Member member = new Member("kth990303", "[email protected]", Platform.KAKAO, "1");
    memberRepository.save(member);

    // 영속성 컨텍스트 비워줌
    em.flush();
    em.clear();
    System.out.println("===========================");
    em.remove(member);
    em.flush();
    em.clear();
}

@Test
@DisplayName("jpql의 delete는 1차 캐시가 비어있을 때 delete 쿼리 뿐만 아니라 select 쿼리도 발생시킨다")
void test4() {
    // 영속성 컨텍스트, DB에 모두 member 저장 (IDENTITY 전략)
    final Member member = new Member("kth990303", "[email protected]", Platform.KAKAO, "1");
    memberRepository.save(member);

    // 영속성 컨텍스트 비워줌
    em.flush();
    em.clear();
    System.out.println("===========================");
    memberRepository.delete(member);
    em.flush();
    em.clear();
}

@Test
@DisplayName("jpql의 deleteAll은 1차 캐시가 비어있을 때 delete 쿼리 뿐만 아니라 select 쿼리도 발생시킨다")
void test5() {
    // 영속성 컨텍스트, DB에 모두 member 저장 (IDENTITY 전략)
    final Member member = new Member("kth990303", "[email protected]", Platform.KAKAO, "1");
    memberRepository.save(member);

    System.out.println("===========================");
    memberRepository.deleteAll();
    em.flush();
    em.clear();
}

@Test
@DisplayName("jpql의 deleteAll은 DB와 영속성 컨텍스트 모두 비어있을 때에도 select 쿼리를 발생시킨다")
void test6() {
    memberRepository.deleteAll();
    em.flush();
    em.clear();
}

Untitled

JpaRepository의 구현체인 SimpleJpaRepository의 delete method를 보면 em.find() 작업을 거치는 것을 알 수 있다. 이 말인 즉슨, 1차 캐시에 있을 경우 영속성 컨텍스트의 값을, 1차 캐시에 없을 경우 select 쿼리를 날려서 찾아온다.

@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));
}

1차 캐시가 비어있고 쓰기 지연 저장소에 delete 쿼리가 있는 상태에서 em.find()를 할 경우 select 쿼리를 쓰기지연저장소에서 커밋하기 전에 날린다