JPQL - 2

Jaca·2021년 8월 29일
0

경로 표현식

경로 표현식이란 위와 같다. 아주 간단해보이지만,
어떤 필드로 연결 되느냐에 따라 내부적으로 아주 다른 결과를 보이므로 잘 알아야한다.

  • 상태 필드 : 단순히 값을 저장하기 위한 필드

  • 연관 필드 : 연관관계를 위한 필드

    • 단일 값 연관 필드 : @ManyToOne, @OneToOne, 대상이 엔티티
    • 컬렉션 값 연관 필드 : @OntToMany, @ManyTOMany, 대상이 컬렉션

경로 표현식 특징

  • 상태 필드 : 경로 탐색의 끝, 더이상 탐색하지 않음

  • 단일 값 연관 경로 : 묵시적 내부 조인 발생, 탐색을 진행

  • 컬렉션 값 연관 경로 : 묵시적 내부 조인 발생, 탐색하지 않음

    • from 절에서 명시적 조인을 통해 별칭을 얻으면 별칭을 통해 탐색 가능

묵시적 내부 조인이란 무엇일까?

List<Team> resultList = em.createQuery("select m.team from Member m", Team.class)
	.getResultList();

이 쿼리를 실행 시키면

Member의 연관관계인 Team을 가져오려고 하니, inner join을 통해 Team을 from절에 Team의 정보를 나열하고 있다.

이 것을 묵시적 내부 조인이라 한다.

이렇게 우리가 작성한 JPQL 쿼리와 실제 SQL 쿼리가 다르게 나가는 것을 조심해야한다.
유지 보수나 튜닝등이 매우 어렵다.

컬렉션 값 연관경로 또한 묵시적 내부 조인이 발생하나, 컬렉션 자체에서 값을 다룰수 없어 탐색은 더이상 진행되지 않는다. (size만 사용가능)

실무에서는 묵시적 내부 조인의 사용보다는 명시적 조인을 추천한다.

페치 조인

페치(fetch)는 앞에서도 보았듯 지연이다.
말 그대로 보자면 지연 조인일 것이다.

페치 조인은 SQL의 조인 종류가 아니다.
JPQL에서 성능 최적화를 위해 제공하는 기능이며, 연관된 엔티티나 컬렉션을 SQL 한 번에 함께 조회하는 기능이다.

페치 조인을 간단하게 이해하자면,
쿼리로 내가 원하는 객체 그래프를 한번에 조회 하는 것을 내가 원하는 타이밍에 동적으로 정할 수 있는 것이다.

예시 상황)

위와 같이 세팅하고 이 쿼리를 날리면 주석에 남긴 것 처럼
Member를 select하는 쿼리까지 SQL을 3번 날린다.
최악의 경우 모두 팀이 다르게 된다면 SQL은 N+1번 날리게 된다.

이러한 성능 저하를 막기 위해 fetch를 사용한다.

select문을 한번밖에 날리지 않았다. m.team을 같이 들고 오되 필요할 때 쓴다는 것이다.
참고로 연관 관계에서 LAZY 세팅을 하든 말았든 join fetch 속성이 우선이다.

컬렉션 페치 조인

컬렉션을 페치 조인 해보았다.
원하는 결과는 나왔으나 teamA가 중복으로 2번 나왔다.

이는 DB에서 1:다 조인을 하면 데이터가 늘어나게 된다.

팀A의 입장에서는 회원은 2명이기 때문에 조인 테이블을 보면 중복되어 나타나게 된다.

그래서 같은 결과를 갖는 중복된 결과를 가지게 되는 것이다.

이 중복을 어떻게 제거할수 있을까.. SQL문의 DISTINCT?

페치 조인과 DISTINCT

DISTINCT 속성을 추가해보자.

하지만 이는 멤버의 값이 다르기 때문에 실제로 다른 값이라는 것.
그래서 SQL상에서 우리가 원하는 결과를 얻을 수 없다.

하지만 DISTINCT가 추가로 애플리케이션에서 중복 제거시도한다.

그래서 실제로 해본다면 우리가 원하는 결과를 얻을 수 있다.

페치 조인과 일반 조인의 차이

페치 조인을 사용하지 않고 그냥 조인을 사용하게되면..


아래와 같이 쿼리가 두번 나감을 볼 수 있다.
왜냐하면 일반 조인을 사용하게되면
JPQL은 결과를 반환할 때 연관관계 고려하지 않는다.
단지 select 절에 지정한 엔티티만 조회할 뿐, 여기서는 팀 엔티티만 조회하고, 회원 엔티티는 조회하지 않는다.

페치 조인의 한계

  • 페치 조인의 대상에는 별칭을 줄 수 없다.

  • 둘 이상의 컬렉션은 페치 조인 할 수 없다.

  • 컬렉션을 페치 조인하면 페이징 API(setFirstResult,
    setMaxResults)를 사용할 수 없다.

정리

  • 모든 것을 페치 조인으로 해결할 수 는 없음

  • 페치조인은 객체그래프를 유지할 때 사용하면 효과적

  • 여러 테이블을 조인해서 엔티티가 가진 모양이 아닌 전혀 다른 결과를 내야 하면, 페치 조인 보다는 일반 조인을 사용하고 필요 한 데이터들만 조회해서 DTO로 반환하는 것이 효과적

Named 쿼리

Named 쿼리란, 미리 정의해서 이름을 부여해두고 사용하는 JPQL 쿼리다.
정적쿼리이며, 어노테이션, XML에 정의할 수 있다.
애플리케이션 로딩 시점에 초기화 후 재사용 애플리케이션 로딩 시점에 쿼리를 검증한다.

XML 설정이 항상 우선권을 가진다.
애플리케이션 운영 환경에 따라 다른 XML을 배포할 수 있다.

벌크 연산

한번에 여러개의 연산을 동시에 적용하고 싶을 때 사용하는 연산이다.
.executeUpdate() 메소드를 사용하며 영향받은 엔티티 수를 반환 한다.
UPDATE, DELETE, INSERT(insert into .. select, 하이버네이트 지원)를 지원한다.

벌크 연산은 영속성 컨텍스트를 무시하고 데이터베이스에 직접 쿼리를 보낸다.
그래서 벌크 연산을 하면 실제 DB에는 값은 변경되었으나, 영속성 컨텍스트엔 적용되지않아 벌크연산을 먼저 실행하거나, 벌크 연산 수행 후 영속성 컨텍스트를 반드시 초기화 해야한다.

profile
I am me

0개의 댓글