개요
스터디에서 Spring-JPA 환경에서, 쿼리메소드와 @Query 어노테이션을 이용한 JPQL, 네이티브 쿼리을 활용한 조회에 대해 이야기를 하다가, 3개의 조회방법 중 어떤 방법이 영속될까 라는 궁금증이 생겼다.
위에서 언급했던 3가지 조회 방법에 대해 영속성 여부을 알아보기로 한다
- 쿼리메서드
- Query 어노테이션을 활용한 JPQL
- Query 어노테이션을 활용한 네이티브 쿼리
테스트
관련 내용을 리서치해보는 것도 좋지만, 일단 아묻따 테스트코드를 작성해서 영속성 여부를 확인하였다.
사용할 테이블은 회원 정보가 있는 테이블이고, Pk는 memberId이다.
세팅
@Autowired
private MemberRepository memberRepository;
@Autowired
private EntityManager entityManager;
쿼리메서드
가장 먼저 가장 많이 사용하는 쿼리메서드를 통한 테스트이다.
@Test
@Transactional(readOnly = true)
void 쿼리메소드_영속성_테스트() {
Member byMemberIdUseQueryMethod = memberRepository.findByMemberId("ksh001");
assertThat(entityManager.contains(byMemberIdUseQueryMethod)).isTrue();
}
쿼리 메서드(findByMemberId)를 활용한 조회는 엔티티매니저 관리하에 있다.
참고로, @Transactional AOP가 적용되어, 해당 클래스의 프락시 객체에 entityManager 생성 코드가 데코레이션 되어 있다. 그렇기 때문에, 해당 어노테이션이 없으면 영속되지 않는다.
JPQL
먼저 memberRepository
에 @Query
어노테이션을 활용하여, JPQL 쿼리를 만든다.
@Query("SELECT p FROM Member p WHERE p.memberId = :memberId")
Member findByMemberIdUseJPQL(@Param("memberId") String memberId);
@Test
@Transactional(readOnly = true)
void JPQL_영속성_테스트() {
Member byMemberIdUseJPQL = memberRepository.findByMemberIdUseJPQL("ksh001");
assertThat(entityManager.contains(byMemberIdUseJPQL)).isTrue();
}
JPQL
을 사용한 조회 코드이다. 마찬가지로, 엔티티매니저 관리하에 있다.
Native Query
@Test
@Transactional
void 트랜잭션적용_네이티브쿼리메소드_테스트() {
Poker7Rat byMemberIdUseJPQL = poker7RatRepository.findByMemberIdUseNativeQuery("ksh001");
assertThat(entityManager.contains(byMemberIdUseJPQL)).isTrue();
}
native Query
을 사용한 조회 코드이다. 조회된 entity도 엔티티매니저 관리하에 있다.
결과적으로 3개 방법을 사용한 조회 모두 영속화된 entity
가 return
된다.
그렇다면, 어떤 방식으로 조회하더라도, 영속화되는 걸까? 지금까지는 식별자(PK)(memberId
)을 통해 조회해 봤다. 이번에는 식별자가 아닌 조건으로 조회해 보자.
식별자(PK)가 아닌 조건으로 조회
@Test
@Transactional
void 트랜잭션적용_쿼리메소드_식별자사용X_테스트() {
Poker7Rat byMemberIdUseJPQL = poker7RatRepository.findByMemberNo("1234567");
assertThat(entityManager.contains(byMemberIdUseJPQL)).isFalse();
}
쿼리메서드로 같은 결과 값을 조회해 봤는데, 엔티티매니저 관리하에 있지 않다. 즉 영속화가 되지 않은 entity
가 return
되었다.
즉 영속화 여부는 조회 방법이 아니라, 조회 조건이 식별자냐 아니냐에 따라, 달라지는 것이다.
DB에서 데이터를 가져오는데, 왜 영속될까?
1차 캐시에서 엔티티를 가져오는 건(em.find 활용) 영속되는 건 당연하다.
그런데, JPQL과 네이티브 쿼리는 DB에서 데이터를 가져오는데, 왜 영속되는 걸까? (물론 쿼리메서드 find 내부 코드 보면 1차 캐시에 값이 없으면 DB에서 직접 가져옴)
그 이유는 하이버네이트가 DB에서 데이터를 직접 가져온 후에, 엔티티와 매칭하고, 1차캐시에 집어넣기 때문이다. (이 부분에 대한 자세한 내용은 다음 포스팅 때 다룰 예정)
그렇다면 왜 식별자가 아닌 조건으로 조회하면, 엔티티가 영속화되지 않을까?(1차 캐시 X)
그 이유는 1차 캐시는 식별자를 통해 데이터를 구분하기 때문에. 식별자를 조건으로 조회해야만, 1차 캐시에 저장된다.
식별자가 아닌 조건으로 조회하더라도, return 되는 entity을 1차 캐시에 저장하면 되는 것 아닌가? 이 부분은 다음 포스팅에서 확인
결론
식별자로 조회하고, 트랜잭션 어노테이션을 사용한다면, 1차 캐시에 저장된다.
주의사항
여기서 헷갈리는 부분이 있을 수 있는데, 식별자로 조회하는 경우 1차 캐시에 모두 저장한다. 와 커스텀 쿼리(JQPL
, Native Query
)로 조회하는 경우, 1차 캐시에 있는 entity
을 가져오지 않고, DB에서 가져온다.
즉 쿼리메서드, 커스텀쿼리는 1차 캐시에 모두 저장하지만, 1차 캐시를 사용하는 건 쿼리메서드뿐이다.
'개발자로서 살아남기 > JPA 이슈 및 최적화' 카테고리의 다른 글
Spring boot + JPA 환경에서 Insert 동시 요청 시 처리방법 (0) | 2023.07.04 |
---|---|
비관적 락, 낙관적 락 사용시 deadlock 관련 이슈들 (0) | 2023.02.28 |
Spring boot + JPA 환경에서 비관적 vs 낙관적 Lock 선택하기 (0) | 2023.02.06 |
Spring Boot + JPA 환경에서 변경 감지(dirty checking)와 병합(merge) (0) | 2023.01.30 |
Spring Boot + JPA + MYSQL 환경에서 saveAll() vs batchInsert() (0) | 2023.01.20 |