@PersistenceContext EntityManager

각 JPA Repository 를 포함한 모든 Bean 에서 같은 인스턴스를 호출함. 즉 모든 상태를 공유한다.


페이징과 정렬 파라미터

  • org.springframework.data.domain.Sort : 정렬을 위한 파라미터
  • org.springframework.data.domain.Pageable : 페이징 값 전달을 위한 파라미터
  • Pageable Interface 는 PageRequest.of(…) 로 Builder 패턴을 통해 객체 획득 가능

페이징 반환 타입

  • org.springframework.data.domain.Page : totalCount 를 추가 조회하여 1, 2, 3, … 등 페이징이 가능한 반환 타입, 일반적인 게시판 형태 (개발자가 호출한 쿼리 이외에 totalCount 를 얻기 위한 쿼리가 1회 더 발생함)
  • org.springframework.data.domain.Slice : totalCount 를 조회하지 않고 개발자가 지정한 pageSize 보다 +1 하여 isLast 여부만 체크, 모바일 환경 등의 스크롤 후 “더 보기” 형태
  • java.util.List : 그냥 페이징 정보를 받지 않고 원 데이터만 반환 (Page 와 Slice 의 경우 .getContent() 를 통해 Wrapping 되어 있는 실제 데이터 획득 가능)

페이징 관련 기타

  • JPA 의 page numbering 은 0 부터 시작한다
  • @Query 시에 countQuery 옵션을 통해 count 용 간소화 쿼리를 별도로 제공할 수 있다
  • (주의!) 절대 Entity Object 를 바로 Response 로 보내지 말 것 (DB 구조를 외부에 그대로 노출하는 꼴이 되며, 또한 DB 구조 변경 시 response 구조 역시 바뀌게 되어 안정적인 API 제공이 불가능하다)

벌크성 수정 쿼리

JPA 의 경우 건 별 update 는 Dirty Checking 을 통해 진행하기에 UPDATE 문 실행을 위해선 별도 쿼리 실행이 필요하다 pure JPA = em.createQuery(…).executeUpdate(); Spring Data JPA = @Modifying @Query(“…”)

@Modifying(clearAutomatically = true)

상기 옵션을 통해 Persistence Context(영속성 컨텍스트)를 통하지 않고 직접 호출한 쿼리와 Persistence Context 1차 캐시의 데이터 간의 불일치를 방지한다. em.clear(); 의 호출과 동일한 효과


FetchType

  • EAGER = 대상 엔티티 조회 시 관계가 있는 타 엔티티를 전부 조회(JOIN)
  • LAZY = 대상이 되는 엔티티만 조회하고, 타 엔티티는 실제 데이터를 호출하는 경우에만 별도로 쿼리 발생 (JPA 에선 일반적으로 FetchType.LAZY 로 설정함)

fetch join 와 @EntityGraph

  • FetchType.LAZY 의 경우 리스트 조회 후 타 엔티티의 데이터를 모두 호출하면 N + 1 문제를 야기할 수 있다.
    (대상 엔티티 리스트 조회로 인한 쿼리 호출 = 1 / 조회된 리스트(크기=N)에 대한 타 엔티티의 데이터 호출로 인한 쿼리 호출 = N) ((정보) 타 엔티티의 데이터 중 중복되는 데이터가 있는 경우 Persistence Context 가 캐싱을 진행하기에 캐싱된 데이터의 경우에는 쿼리를 날리지 않고 캐싱된 데이터를 활용하나, 리스트의 타 엔티티 데이터가 DISTINCT 하다면 최대 N 의 쿼리가 발생한다)

이러한 상황에선 FetchType.EAGER 가 적용되어 JOIN 쿼리가 발생되어야 하나, FetchType 은 호출시점에서 동적으로 바꿀 수 없음.

fetch join

fetch join 은 호출 시 FetchType 이 LAZY 여도 EAGER 인 것과 같이 JOIN 쿼리가 호출될 수 있도록 강제한다.

  • pure JPA : @Query(“select m from Member m join fetch m.team”)
  • Spring Data JPA : @EntityGraph(attributePaths = {“team”}) (쿼리로 작성하는 경우엔 inner, outer join 을 선택할 수 있지만 @EntityGraph 는 무조건 outer join 으로 고정이다)

@NamedQuery 와 같이 EntityGraph 도 엔티티 클래스에 Alias 를 지정하는 @NameEntityGraph 적용할 수 있으나, 많이 안쓴다.

fetch join 사용시 join 으로 인해 실제 엔티티의 row 를 측정할 수 없기에 페이징을 사용할 수 없음. (페이징 대상은 PK 값만 페이징으로 불러오고, fetch join 으로 쿼리 재호출 하여 모든 데이터를 다시 조회해야 하는걸까?)


JOIN 시 중복 데이터 이슈

  • DISTINCT 로 중복 제거
  • 반환 타입을 Set 으로 지정하여 중복 제거

참고

  • https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%8D%B0%EC%9D%B4%ED%84%B0-JPA-%EC%8B%A4%EC%A0%84