JPA 8

QueryHint를 적용하기 전에 고려해볼 사항

변경감지를 위해서는 원본과 현재의 값을 어쩔 수 없이 둘 다 가지고 있어야 합니다. 어떤 부분이 변경되었는지 파악할 수 있어야하니까요. 정말 조회에만 영향이 있는 쿼리라면 별도의 MemberReadOnlyRepository를 분리해서 읽기 전용 조회를 구성할 수도 있습니다. 하지만 쿼리 힌트보다는 1차 캐시를 우선 적용해서 DB로의 쿼리 자체를 줄이는 것을 먼저 적용하는게 우선입니다. redis와 같은 별도의 구성 없이 쿼리힌트만으로 최적화가 될 수 있는지는 성능 테스트를 진행해봐야 합니다. @Repository public interface MemberJpaRepository extends JpaRepository { @QueryHints(value = @QueryHint(name = "org.hibe..

DEV/Spring Data JPA 2023.09.24

@EntityGraph로 fetch join 쉽게 쓰기

@ManyToOne, @OneToOne을 사용할 때는 몇 가지 정해진 규칙이 있습니다. 1. 불필요한 JOIN과 조회가 연속적으로 일어나는 것을 막기 위해 fetchType.LAZY 사용한다. 2. fetchType.LAZY로 인한 N+1 문제를 해결하기 위해 fetch join을 사용한다 쿼리를 작성하다보면 간단한 fetch join을 매번 @Query를 사용해서 JPQL을 직접 작성하기 번거로울 때가 있습니다. 이럴 때는 fetch join을 간단하게 적용하게 도와주는 @EntityGraph를 사용할 수 있습니다. @Repository public interface MemberJpaRepository extends JpaRepository { @Override // @Query("select m fr..

DEV/Spring Data JPA 2023.09.24

Page<Entity>를 API의 응답으로 리턴하는 방법

pagination을 사용해서 조회한 Entity 정보를 API의 응답으로 제공할 때, 의외로 잘 모르시는 부분이 있습니다. Entity를 API의 응답으로 그대로 노출하면 안되기 때문에 아래와 같이 변환하시는 경우가 있습니다. Page memberList = memberJpaRepository.findPageByName("name", PageRequest.of(0, 10)); List memberDto = memberList.getContent() .stream() .map(m -> new MemberReadResponse(m.getId(), m.getName())) .collect(Collectors.toList()); response = new MemberResponseDto(memberDto, m..

DEV/Spring Data JPA 2023.09.24

Join과 Pagination 함께 사용할 때 Count 쿼리 최적화

JOIN을 해서 pagination까지 제공해야하는 경우가 있습니다. @Repository public interface MemberJpaRepository extends JpaRepository { @Query(value = "select m from Member m left join m.team t where t.id = :teamId") Page findPageByName(String name, Pageable pageable); } 그런데 이 때 JOIN을 사용하는 COUNT 쿼리가 발생할 수 있습니다. select count(m) from Member m left join m.team t left outer join은 COUNT 결과 갯수에 영향을 주지 않음에도, 이미 충분히 무거운 count ..

DEV/Spring Data JPA 2023.09.24

COUNT 쿼리, Page<T> vs Slice<T>

데이터를 조회할 때 pagination을 사용해야하는 경우가 있습니다. 전형적인 pagination에서는 데이터의 전체 건수가 필요합니다. 현재 조회하고 있는 데이터가 몇 번째 페이지에 속한 데이터인지 알 수 있어야하기 때문입니다. 그런데 모바일 환경에서는 무한스크롤방식의 UXUI가 많아지면서 전체 데이터 건수는 필요없는 경우가 있습니다. Spring에서는 아래와 같이 Count 조회를 하지 않는 Slice와 Count 조회를 하는 Page를 구분해서 제공하고 있습니다. public interface Page extends Slice { int getTotalPages(); long getTotalElements(); } Slice는 다음 페이지의 존재 여부를 확인하기 위해, 요청받은 데이터 조회 건수 ..

DEV/Spring Data JPA 2023.09.24

데이터 조회의 반환값 T vs Optional<T>

repository 계층에서 반환 타입은 어떤 타입으로 해야할까요? 먼저 EntityManager에서 getSingleResult를 조회했을 때 값이 없는 경우에는 NotResultException이 발생합니다. @Test public void findEntityNotExists(){ //given assertThrows(NoResultException.class, () -> em.createQuery("select m from Member m where m.name = :name", Member.class) .setParameter("name", "asdasdasd") .getSingleResult()); } Spring-data-jpa에서는 NoResultException을 catch해서 null로 ..

DEV/Spring Data JPA 2023.09.24

메서드 이름으로 데이터 조회하기 vs JPQL 직접 작성해서 데이터 조회하기

@Repository public interface MemberJpaRepository extends JpaRepository { List findByNameAndEmail(String name, String email); @Query("select m from Member m where m.name = :name and m.email = :email") List findUser(@Param("name") String name, @Param("email") String email); } findByNameAndEmail은 JpaRepository를 extends한 인터페이스에서 메서드 이름으로 데이터를 조회할 조건을 지정하는 방식입니다. 이 방법은 내부적으로 JPQL을 생성해주는 Spring Data J..

DEV/Spring Data JPA 2023.09.24