[JPA] JPQL (2)
경로 표현식
점을 찍어 상태에 접근하는 방법을 객체 그래프 탐색이라고 부른다.
객체 그래프 탐색으로 필드에 접근하는 요소에 따라 상태 필드 / 단일 값 연관 필드 / 컬렉션 값 연관 필드로 구분된다.
상태 필드는 단순히 값을 저장하기 위한 필드를 의미한다.
연관 필드는 연관관계를 위한 필드로, 엔티티 또는 컬렉션을 대상으로 하는 탐색을 의미한다.
상태 필드는 경로 탐색의 끝으로, 더 이상 탐색할 수 없다. (chaining 불가능)
단일 값 연관 필드로 탐색을 수행하면 묵시적 내부 조인이 발생한다.
따라서 쿼리 튜닝이 어려우니.. 조심해서 사용해야 한다.
컬렉션 값 연관 필드로 탐색 수행 시 더 이상 탐색할 수 없다.
경로 탐색의 끝이 아님에도 탐색이 불가능하니, 꼭 탐색이 필요한 경우 FROM 문법으로 명시적 조인을 통해 별칭을 얻어와야 한다.
( select m.username From Team t join t.members m )
이론은 위와 같은데.. 실무에서는 묵시적 조인을 사용하지 않는 편이 합리적이다.
(명시적 조인은 join 키워드를 직접 사용해서 조인하는 경우를 의미하고, 묵시적 조인은 경로 표현식에 의해 묵시적으로 SQL 조인이 발생하는 경우를 의미한다)
Fetch Join
SQL에서 제공하는 조인 방식은 아니고, JPQL에서 성능 최적화를 위해 제공하는 기능이다.
Fetch Join을 사용하면 연관된 엔티티나 컬렉션을 한 번에 같이 조회할 수 있다.
select m from Member m join fetch m.team
회원을 조회하면서 연관된 Team도 함께 조회하고 싶은 경우 위와 같이 join fetch를 사용한다.
한 번에 조회할 수 있어 쿼리가 나가는 횟수가 엄청나게 줄어들어 그냥 join만 사용할 때 보다 훨씬 효과적이다. (N + 1 문제 해결)
다대일 관계에서는 fetch join을 사용해도 중복 데이터가 추가되지 않지만, 일대다 관계에서는 데이터가 뻥튀기되어 중복 데이터가 추가될 수 있다.
JPQL의 DISTINCT 키워드를 사용해 중복된 결과를 제거하자. (SQL의 DISTINCT에 기능이 추가돼 성공적으로 중복을 제거할 수 있다)
쿼리의 전송 횟수를 획기적으로 줄일 수 있다는 점에서 Fetch Join은 굉장히 중요하고 실무에서 꼭 사용해야 하는 기술이지만, 몇 가지 단점이 있다.
1. 별칭을 설정할 수 없다
Fetch Join은 기본적으로 연관된 요소들을 한 번에 가져올 때 사용한다.
별칭을 사용해 데이터를 필터링한 후 조회하는 방법이 효과적으로 보이지만, 이 부분을 Fetch Join와 함께 사용하는 방식은 JPA가 의도한 방식이 아니다.
필터링 작업은 별도의 쿼리를 작성해서 수행하는 편이 운영 관점에서 합리적이다.
2. 둘 이상의 컬렉션은 Fetch Join이 불가능하다
일대다 관계에서도 데이터가 뻥튀기되는데.. 컬렉션이 둘 이상인 경우 일대다대다 관계가 된다..
데이터의 정합성을 위해 Fetch Join의 컬렉션은 하나만 지정할 수 있다고 기억하자.
3. 컬렉션을 Fetch Join 시 페이징 API를 사용할 수 없다
페이징은 데이터베이스 중심으로 작동한다.
일대일 다대일 설계처럼 단일 값 연관 필드는 Fetch Join해도 페이징이 가능하지만, 일대다 설계의 경우 데이터가 뻥튀기된다.
다대일 설계로 방향을 뒤집는 방법도 있지만, 뒤집기가 불가능한 경우 Fetch Join을 포기하고 @BatchSize 애너테이션을 사용해서 해결할 수 있다.
쿼리를 테이블 수로 맞출 수 있어 N+1 문제를 해결할 수 있다.
Fetch Join은 Lazy 로딩보다 우선해서 수행되니 기본적으로는 Lazy 로딩으로 설정하고 최적화가 필요한 부분에서 Fetch Join을 설정하는 방향으로 설계하자.
엔티티 직접 사용
JPQL에서 엔티티를 직접 사용하는 경우 SQL로 변환될 때 해당 엔티티의 PK를 사용하도록 설정된다.
위와 같이 엔티티를 파라미터로 직접 전달하든 엔티티 식별자를 전달하든 SQL은 PK를 사용한다.
Named 쿼리
미리 정의해놓고 이름을 부여해서 사용하는 JPQL이다.
정적 쿼리에서만 가능하고 애너테이션이나 XML에 정의하고 사용한다.
애플리케이션이 로딩되는 시점에 쿼리를 검증할 수 있어 실수 방지에 매우 효과적이다.
위와 같이 @NamedQuery 애너테이션을 통해 사용하는 쿼리를 name 변수에 저장한 후 활용한다.
쿼리에 오타가 있을 때 컴파일 시점에서 잡을 수 있다.
벌크 연산
벌크 연산을 사용하면 한 번의 쿼리로 여러 테이블을 변경할 때 사용할 수 있다.
영속성 컨텍스트를 무시하고 데이터베이스에 직접 쿼리를 보내는 방식으로 동작하기 때문에 영속성 컨텍스트와 충돌하지 않도록 적절하게 사용해야 한다.
벌크 연산을 먼저 수행하고 영속성 컨텍스트를 초기화하는 방식을 사용하자.
테이블을 하나씩 변경해야 하는 경우 쿼리도 하나씩 보내야 하지만, 벌크 연산을 사용하면 한 번에 처리할 수 있어 매우 효과적이다.
데이터베이스와 객체의 정보가 일관성을 유지하도록 하기 위해 영속성 컨텍스트를 적절히 초기화하면서 사용하자.
'Spring > JPA' 카테고리의 다른 글
[JPA] OSIV (0) | 2022.12.31 |
---|---|
[JPA] JPQL (1) (0) | 2022.12.27 |
[JPA] 값 타입 (0) | 2022.12.26 |
[JPA] 영속성의 전이와 고아 객체 (0) | 2022.12.24 |
[JPA] 프록시와 연관관계 (0) | 2022.12.24 |
댓글
이 글 공유하기
다른 글
-
[JPA] OSIV
[JPA] OSIV
2022.12.31 -
[JPA] JPQL (1)
[JPA] JPQL (1)
2022.12.27 -
[JPA] 값 타입
[JPA] 값 타입
2022.12.26 -
[JPA] 영속성의 전이와 고아 객체
[JPA] 영속성의 전이와 고아 객체
2022.12.24