해결된 질문
작성
·
28
·
수정됨
0
@Service
@RequiredArgsConstructor
public class MemberService {
private final MemberRepository memberRepository;
@Transactional //변경
public Long join(Member member) {
validateDuplicateMember(member); //중복 회원 검증
memberRepository.save(member);
return member.getId();
}
}
@Repository
@RequiredArgsConstructor
public class MemberRepository {
private final EntityManager em;
public void save(Member member) {
em.persist(member);
}
public Member findOne(Long id) {
return em.find(Member.class, id);
}
}
---------------------------------------------------------------------------
// 테스트 코드
@Transactional
public class MemberServiceTest {
@Autowired MemberService memberService;
@Autowired MemberRepository memberRepository;
@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));
}
}
회원 도메인 개발 파트의 마지막 강의에 있는 테스트 부분입니다
테스트는 성공이고, 테스트 결과 로그에는 member 객체에 대한 select sql이 찍힙니다
그런데 테스트 로그에 왜 select sql이 나오는지 잘 모르겠습니다
제가 이해하고 있는 내용은 이렇습니다
//When
Long saveId = memberService.join(member);
위 코드에서 insert sql 이 생성되지만 내부 트랜잭션이라 커밋하지 않아서 해당 엔티티가 영속성 컨텍스트에만 추가된 상태
//Then
assertEquals(member, memberRepository.findOne(saveId));
위 코드에선 영속성 컨텍스트의 member를 가져오니 select sql 이 실행되지 않습니다
//When
Long saveId = memberService.join(member);
//Then
assertEquals(member, memberRepository.findOne(saveId));
그리고 테스트 코드에서 @Transactional 은 롤백이니 join()의 결과가 롤백됩니다
join() 은 롤백됬으니 테스트 결과 로그에 insert sql 이 나오지 말아야 하고,
영속성 컨텍스트의 member를 조회했으니 select sql 도 나오지 말아야 하는 것으로 예상됩니다
하지만 insert sql 은 예상한대로 테스트 결과에 나오지 않는데,
select sql 은 나오는 이유를 모르겠습니다
해결했습니다
validateDuplicateMember(member)
중복 회원 검증하는 메서드에서 select sql을 호출하고 있었습니다
em.persist() 보다 먼저 호출하기 때문에 DB에서 바로 조회해오고 있었습니다
이걸 못보다니..
답변 1
0
안녕하세요, 인프런 AI 인턴입니다.
pansakr님께서 질문하신 내용은 JPA를 사용한 테스트 코드에서 select 쿼리가 발생하는 이유와 관련이 있습니다. 이와 관련해 트랜잭션의 동작 방식과 JPA의 조회 과정에 대해 설명드리겠습니다.
insert 쿼리 발생 여부:
@Transactional
어노테이션이 있는 경우, 스프링은 테스트 메서드가 완료된 후 자동으로 트랜잭션을 롤백합니다. 따라서 트랜잭션이 종료되기 전까지는 insert
쿼리가 데이터베이스에 영구적으로 반영되지 않습니다.select 쿼리 발생 이유:
assertEquals
메서드는 memberService.join(member);
에서 반환된 saveId
를 사용하여 memberRepository.findOne(saveId);
메서드를 호출합니다.findOne
메서드는 JPA의 EntityManager.find
를 호출하는데, 이는 데이터베이스에서 해당 ID의 Member
를 조회하게 됩니다.select
쿼리가 발생하게 됩니다.트랜잭션과 롤백:
@Transactional
에 의해 롤백됨으로써 데이터베이스의 상태는 변경되지 않습니다. 그러나 쿼리 자체는 테스트 도중에 실행되어 로그에 남게 됩니다.JPA에서는 첫 번째 레벨 캐시 전략을 사용하여, 영속성 컨텍스트에 엔티티가 없는 경우 데이터베이스에 쿼리를 발생시킵니다. 따라서 테스트 환경에서 “select” 쿼리가 발생하는 것은 당연한 현상입니다.
회원 도메인 테스트 시에 발생하는 쿼리 수행 및 롤백 관련해서 더 깊이 이해하기 위해 아래 유사한 질문과 답변을 참고해보시기 바랍니다:
이 링크에서는 트랜잭션의 처리, 쿼리 발생 여부, JUnit과 JPA의 상호 작용에 대해 논의되고 있습니다.
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.
memberService.join(member) 실행했을때, 영속성 컨텍스트에 member 가 저장되니까
findOne(saveId) 실행했을땐 영속성 컨텍스트에서 member 를 가져오는게 아닌가요?
그래서 select sql 이 나가지 말아야 하는것으로 이해하고 있는데
결과는 select sql 이 실행되니 제가 어느 부분을 잘못 알고 있는지 모르겠어요
select sql이 실행된다는 의미는 영속성 컨텍스트에 member 가 없어서 DB에서
조회한다는 것인데 다시 봐도 영속성 컨텍스트에 member 가 있는 상황이라서
왜 select sql 이 발생하는지 모르겠습니다