경로 표현식
-점을 찍어 객체 그래프를 탐색하는 것
select m.username (상태 필드)
from Member m
join m.team t (단일 값 연관 필드)
join m.orders o (컬렉션 값 연관 필드)
where t.name = '팀A'
-상태 필드(state field): 단순히 값을 저장하기 위한 필드
-연관 필드(association field): 연관관계를 위한 필드
-단일 값 연관 필드: @ManyToOne, @OneToOne, 대상이 엔티티
-컬렉션 값 연관 필드: @OneToMany, @ManyToMany, 대상이 컬렉션
경로 표현식 특징
-상태 필드: 경로 탐색의 끝, 탐색 못함
-단일 값 연관 경로: 묵시적 내부 조인(inner join) 발생, 탐색 O
-묵시적은 가급적 안쓰는 방향(JPQL와 SQL를 유사하게 짜야함)
-컬렉션 값 연관 경로: 묵시적 내부 조인 발생, 탐색 못함
-FROM절에서 명시적 조인을 통해 별칭을 얻으면 별칭을 통해 탐색 가능
-e.g.) select m.username From Team t join t.members m
명시적 조인, 묵시적 조인
-명시적 조인: join 키워드 직접 사용
-e.g.) select m from Member m join m.team t
-묵시적 조인: 경로 표현식에 의해 묵시적으로 SQL 조인 발생
-e.g.) select m.team from Member m
JPQL 페치 조인(fetch join) : 실무 필수
-SQL 조인 종류 X
-JPQL에서 성능 최적화를 위해 제공하는 기능
-연관된 엔티티나 컬렉션을 SQL 한번에 함께 조회하는 기능
-join fetch 명령어
-페치 조인 ::= [ LEFT [OUTER] | INNER ] JOIN FETCH 조인경로
엔티티 페치 조인
-회원을 조회하면서 연관된 팀도 함께 조회
-(JPQL) select m from Member m join fetch m.team
-(SQL) SELECT M.* T.* FROM MEMBER M INNER JOIN TEAM T ON M.TEAM_ID = T.ID
-지연로딩으로 쿼리가 N+1개 발생 -> 무조건 fetch join으로 해결
컬렉션 페치 조인
-일대다 관계
-(JPQL) select t from Team t join fetch t.members where t.name = '팁A'
-(SQL) SELECT T.* M.* FROM TEAM T INNER JOIN MEMBER M ON T.ID = M.TEAM_ID WHERE T.NAME = '팀A'
-DB 입장에서 컬렉션 조인하면 데이터 뻥튀기 됨 (e.g. 팀 입장에서는 하난데 컬렉션 크기에 따라 테이블 행이 추가)
페치 조인과 DISTINCT
-SQL의 DISTINCT는 중복된 결과를 제거하는 명령(SQL에서는 완전히 똑같아야 제거)
-JPQL의 DISTINCT 2가지 기능 제공
-1.SQL에 DISTINCT를 추가
-2.애플리케이션에서 엔티티 중복 제거(같은 식별자를 가진 엔티티 제거)
페치 조인과 일반 조인의 차이
-일반 조인은 실행시 연관된 엔티티를 함께 조회하지 않음
-(JPQL) select t from Team t join t.members m where t.name = '팀A' ( Team만 퍼올리고 Member는 초기화가 안돼서 쿼리 따로 나가야함 페치 조인은 쿼리 한방에 끝남)
-JPQL은 결과를 반환할때 연관관계 고려X
-단지 SELECT절에 지정한 엔티티만 고려 할뿐
-페치 조인을 사용할때만 연관된 엔티티도 함께 조회(즉시 로딩)
-페치 조인은 객체 그래프를 SQL 한번에 조회하는 개념(실무) -> N+1 문제 해결
페치 조인의 특징과 한계
-페치 조인 대상에는 별칭을 줄수 없음
-둘 이상의 컬렉션은 페치 조인 할수 없음
-컬렉션을 페치 조인하면 페이징 API를 사용할수 없음
-일대일, 다대일 같은 단일 값 연관필드들은 페치 조인해도 페이징 가능(데이터 뻥튀기가 안되니까)
-일대다는 @BatchSize(size = 100)을 이용해서 배치만큼 퍼올려 페치 조인 하여 해결 가능
-엔티티에 직접 적용하는 글로벌 로딩 전략(LAZY)보다 우선순위가 높음
-최적화가 필요한 곳(N+1)은 페치 조인 적용
-페치 조인은 객체 그래프를 유지할때 사용하면 효과적
-여러 테이블은 조인해서 엔티티가 가진 모양이 전혀 아닌 다른 결과를 내야하면, 페치 조인보다는 일반 조인을 사용하고 필요한 데이터들만 조회해서 DTO로 반환하는것이 효과적
다형성 쿼리
-TYPE
-조회 대상을 특정 자식으로 한정
-(JPQL) select i from Item i where type(i) IN (Book, Movie)
-(SQL) select i from i where i.DTYPE in ('B', 'M')
-TREAT
-자바의 타입 캐스팅과 유사
-상속 구조에서 부모 타입을 특정 자식 타입으로 사용하고 싶을때
-FROM, WHERE, SELECT 사용
-(JPQL) select i from Item i where treat(i as Book).auther = 'kim'
-(SQL) select i.* from Item i where i.DTYPE = 'B' and i.auther = 'kim'
엔티티 직접 사용-기본 키 값
-JPQL에서 엔티티를 직접 사용하면 SQL에서 해당 엔티티의 기본 키 값을 사용
-파라미터(setParameter())로 전달해도 됨
-(JPQL) select count(m.id) from Member m
-(JPQL) select count(m) from Member m
-(두 JPQL 모두 다음 SQL) select count(m.id) as cnt from Member m
Named 쿼리
-미리 정의해서 이름을 부여해두고 사용하는 JPQL
-정적 쿼리
-어노테이션, XML에 정의
-@NameQuery(
name = "Member.findByUsername",
query = "select m from Member m where m.username = :username")
-애플리케이션 로딩 시점에 초기화 후 재사용
-애플리케이션 로딩 시점에 쿼리를 검증(컴파일 시점에 오류 발견가능)
-스프링 부트에서 편리하게 QUERY로 바로 사용
벌크 연산
-한건을 뽑아보는 것을 제외한 모든 update, delete문
-변경된 데이터가 100건이라면 100번의 UPDATE SQL 발생하는 문제
-쿼리 한번으로 여러 테이블 Row 변경
-executeUpdata()의 결과는 영향받은 엔티티 수 반환
-UPDATE, DELETE 지원
-INSERT(insert into .. select, 하이버네이트 지원)
벌크 연산 주의
-벌크 연산은 영속성 컨텍스트를 무시하고 데이터베이스에 직접 쿼리
-해결책
-1.벌크 연산을 먼저 실행(맨 처음 실행시)
-2.벌크 연산 수행후 영속성 컨텍스트 초기화(DB는 변했어도 영속성 컨텍스트는 이전값이므로 clear()로 초기화)
'웹 프로그래밍 > JPA' 카테고리의 다른 글
JPA 활용(2) 팁 정리 (0) | 2023.03.16 |
---|---|
JPA 활용(1) 팁 정리 (0) | 2023.03.08 |
객체 지향 쿼리 언어_기본 (0) | 2023.03.07 |
기본값 타입, 임베디드 타입 (0) | 2023.03.06 |
영속성 전이(CASCADE)와 고아 객체 (0) | 2023.03.04 |