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

constela10님의 프로필 이미지

작성한 질문수

실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발

select 쿼리가 나가는 이유

해결된 질문

작성

·

87

0

섹션 4. 회원 도메인 개발 회원 기능 테스트 강좌 관련 질문입니다.

 

@Test
public void 회원가입() throws Exception {
    // given
    Member member = new Member();
    member.setName("kim");

    // when
    Long saveId = memberService.join(member);

    // then
    assertEquals(member, memberRepository.findOne(saveId));
}

위의 코드를 실행시켰을 때, insert문 쿼리가 왜 나가지 않는지는 이해를 했습니다. 하지만 select 쿼리가 왜 나가는지는 궁금합니다.
제가 생각한 흐름을 설명드리면,
memberService의 join 함수를 실행해서 member 객체를 영속화하게 되면 1차 캐시에 해당 데이터가 저장이 될 것이고 동시에 쓰기 지연 SQL 저장소에 insert 쿼리문이 쌓입니다. 이때 쿼리문은 트랜잭션이 커밋되면 DB에 반영이 되지만 테스트 클래스에서는 트랜잭션이 롤백되기 때문에 insert 쿼리가 나가지 않게 됩니다. 이후 findOne 메소드를 실행할 때에는 DB에서 직접 찌르는 것이 아니라 1차 캐시에 저장되어 있기 때문에 1차 캐시에서 데이터를 가져와도 되기 때문에 select문이 나가지 않아도 된다고 생각했는데 실제 실행결과를 보니 select 쿼리문이 나가더군요.. 이유가 궁금합니다

답변 2

0

김영한님의 프로필 이미지
김영한
지식공유자

안녕하세요. constela10님

join() 메서드를 보시면 validateDuplicateMember()를 호출합니다.

이 메서드는 내부에서 findByName을 호출하는데요. 이때 JPQL이 사용됩니다.

JPQL은 영속성 컨텍스트와 무관하게 항상 SQL을 먼저 실행하게 됩니다.

감사합니다.

0

안녕하세요, 인프런 AI 인턴입니다.

constela10님의 질문에 대한 답변입니다.

회원 가입 테스트 과정에서 select 쿼리가 발생하는 주된 이유는 JPA의 동작 방식 때문입니다. JPA를 사용할 때, 객체를 저장하는 join 메소드와 같은 경우 쓰기 지연 SQL 저장소에 insert 쿼리를 쌓고, 트랜잭션이 커밋되는 시점에 해당 쿼리를 데이터베이스에 반영합니다.

하지만 findOne 메소드를 호출할 때, JPA는 해당 엔티티가 1차 캐시에 존재하는지 확인합니다. 1차 캐시에 해당 데이터가 없으면, 데이터베이스에 실제로 해당 데이터가 존재하는지 확인하기 위해 select 쿼리를 발생시킵니다. 테스트 환경에서는 특정 설정(예: @Transactional)에 의해 실제 데이터베이스에 insert가 반영되지 않고 롤백되기 때문에, insert 쿼리는 보이지 않습니다.

테스트 클래스가 기본적으로 트랜잭션을 롤백하는 동작을 수행하기 때문에, insert 쿼리는 실행되지만 실제 데이터베이스에 반영되지 않습니다. 그렇기 때문에 findOne을 호출했을 때, JPA는 이미 영속성 컨텍스트에 해당 엔티티가 있는지 확인 후 없다고 판단하고 데이터베이스에서 직접 조회하기 위해 select 쿼리를 실행하게 됩니다.

이런 동작은 JPA가 보장하는 영속성 컨텍스트 내의 1차 캐시의 일관성을 유지하기 위해 필요한 절차입니다. 만약 join 메소드 실행 시 영속화된 엔티티의 식별자가 데이터베이스에 이미 존재한다면, JPA는 insert가 아닌 select를 먼저 호출하여 해당 식별자의 엔티티가 이미 있는지 검증합니다. 이 과정에서도 select 쿼리가 발생할 수 있습니다.

이 설명이 constela10님의 궁금증을 해결하는데 도움이 되었기를 바랍니다.